Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MonadFilter docs and comprehension guards #441

Merged
merged 8 commits into from
Nov 5, 2017

Conversation

raulraja
Copy link
Member

@raulraja raulraja commented Nov 5, 2017

This PR Fixes #336 and enables continueIf and bindWithFilter on monad comprehensions for instances adhering to MonadFilter which can provide an empty F<A> for a given Monad.
Docs with examples shown below


MonadFilter

MonadFilter is a type class that abstract away the option of interrupting computation if a given predicate is not satisfied.

All instances of MonadFilter provide syntax over their respective data types to comprehend monadically over their computation:

continueWith

Binding over MonadFilter instances with bindingFilter brings into scope the continueIf guard that requires a Boolean predicate as value. If the predicate is true the computation will continue and if the predicate returns false the computation is short-circuited returning monad filter instance empty() value.

In the example below we demonstrate monadic comprehension over the MonadFilter instances for both Option and ListKW since both data types can provide a safe empty value.

When continueIf is satisfied the computation continues

import kategory.*

Option.monadFilter().bindingFilter {
 val a = Option(1).bind()
 val b = Option(1).bind()
 val c = a + b
 continueIf(c > 0)
 yields(c)
}
//Some(value=2)
ListKW.monadFilter().bindingFilter {
 val a = listOf(1).k().bind()
 val b = listOf(1).k().bind()
 val c = a + b
 continueIf(c > 0)
 yields(c)
}
//ListKW(list=[2])

When continueIf returns false the computation is interrupted and the empty() value is returned

Option.monadFilter().bindingFilter {
 val a = Option(1).bind()
 val b = Option(1).bind()
 val c = a + b
 continueIf(c < 0)
 yields(c)
}
//None
ListKW.monadFilter().bindingFilter {
 val a = listOf(1).k().bind()
 val b = listOf(1).k().bind()
 val c = a + b
 continueIf(c < 0)
 yields(c)
}
//ListKW(list=[])

bindWithFilter

Binding over MonadFilter instances with bindingFilter brings into scope the bindWithFilter guard that requires a Boolean predicate as value getting matched on the monad capturing inner value. If the predicate is true the computation will continue and if the predicate returns false the computation is short-circuited returning the monad filter instance empty() value.

When bindWithFilter is satisfied the computation continues

Option.monadFilter().bindingFilter {
 val a = Option(1).bind()
 val b = Option(1).bindWithFilter { it == a } //continues
 val c = a + b
 yields(c)
}
//Some(value=2)
ListKW.monadFilter().bindingFilter {
 val a = listOf(1).k().bind()
 val b = listOf(1).k().bindWithFilter { it == a } //continues
 val c = a + b
 yields(c)
}
//ListKW(list=[2])

When bindWithFilter returns false the computation short circuits yielding the monad's empty value

Option.monadFilter().bindingFilter {
 val a = Option(0).bind()
 val b = Option(1).bindWithFilter { it == a } //short circuits because a is 0
 val c = a + b
 yields(c)
}
//None
ListKW.monadFilter().bindingFilter {
 val a = listOf(0).k().bind()
 val b = listOf(1).k().bindWithFilter { it == a } //short circuits because a is 0
 val c = a + b
 yields(c)
}
//ListKW(list=[])

@raulraja raulraja requested a review from a team November 5, 2017 20:44
@codecov-io
Copy link

codecov-io commented Nov 5, 2017

Codecov Report

Merging #441 into master will decrease coverage by 0.03%.
The diff coverage is 27.58%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master     #441      +/-   ##
============================================
- Coverage     36.29%   36.25%   -0.04%     
- Complexity      312      320       +8     
============================================
  Files           173      174       +1     
  Lines          4794     4821      +27     
  Branches        515      523       +8     
============================================
+ Hits           1740     1748       +8     
- Misses         2910     2925      +15     
- Partials        144      148       +4
Impacted Files Coverage Δ Complexity Δ
kategory-docs/src/main/java/kategory/debug.kt 0% <ø> (ø) 0 <0> (ø) ⬇️
...t/src/main/kotlin/kategory/laws/MonadFilterLaws.kt 3.57% <0%> (-3.58%) 1 <0> (ø)
...rc/main/kotlin/kategory/typeclasses/MonadFilter.kt 80% <100%> (+30%) 0 <0> (ø) ⬇️
...in/kategory/typeclasses/MonadFilterContinuation.kt 50% <50%> (ø) 8 <8> (?)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 8b9a098...4358f6e. Read the comment docs.

open class MonadFilterContinuation<F, A>(val MF: MonadFilter<F>, override val context: CoroutineContext = EmptyCoroutineContext) :
MonadContinuation<F, A>(MF) {

object PredicateInterrupted : RuntimeException()
Copy link
Member

@pakoito pakoito Nov 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if this is just for internal use, please add an error description. I'd also make the object internal or even private.

Law("MonadFilter Laws: Right Empty", { monadFilterRightEmpty(MF, cf, EQ) }),
Law("MonadFilter Laws: Consistency", { monadFilterConsistency(MF, cf, EQ) }))
Law("MonadFilter Laws: Right Empty", { monadFilterRightEmpty(MF, EQ) }),
Law("MonadFilter Laws: Consistency", { monadFilterConsistency(MF, cf, EQ) }),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use applicative pure instead of constructor function?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just removed that cf param because it was not used but did not added anything to that in this PR. But presumably yes.

@@ -5,3 +5,103 @@ permalink: /docs/typeclasses/monadfilter/
---

## MonadFilter

`MonadFilter` is a type class that abstract away the option of interrupting computation if a given predicate is not satisfied.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: that abstracts

Copy link
Member

@pakoito pakoito left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just a couple of nits

@pakoito
Copy link
Member

pakoito commented Nov 5, 2017

I meant a description message in RuntimeException, not on the documentation, but close enough hahahaha

@raulraja raulraja merged commit 24b9600 into master Nov 5, 2017
@raulraja raulraja deleted the rr-monad-filter-comprehensions-guards branch November 5, 2017 22:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants