-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Add documentation for Bimonad
#4076
Changes from 1 commit
9adfc5e
5d0d8ef
0a4ea09
030405c
d4d97b2
3f7180a
687dd0b
ea8322d
0ab5e3f
2923299
896b945
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,41 +23,41 @@ def f[T[_] : Bimonad, S](fa: T[S]): S | |
keep in mind `Bimonad` has its own added laws so something that is both monadic | ||
and comonadic may not necessarily be a lawful `Bimonad`. | ||
|
||
###Eval as a Bimonad | ||
Eval is a lawful `Bimonad` so you can chain computations (like a `Monad`) and `extract` the result at the end (like a `Comonad`). | ||
### NonEmptyList as a Bimonad | ||
NonEmptyList is a lawful `Bimonad` so you can chain computations (like a `Monad`) and `extract` the result at the end (like a `Comonad`). | ||
|
||
Here is a possible implementation based on existing monad and comonad: | ||
```scala mdoc | ||
import cats._ | ||
import cats.data._ | ||
import cats.implicits._ | ||
|
||
implicit def evalBimonad(implicit monad: Monad[Eval], comonad: Comonad[Eval]) = | ||
new Bimonad[Eval] { | ||
implicit def nelBimonad(implicit monad: Monad[NonEmptyList], comonad: Comonad[NonEmptyList]) = | ||
new Bimonad[NonEmptyList] { | ||
|
||
//use Eval specific methods for creation and extraction | ||
override def pure[A](a: A): Eval[A] = | ||
Eval.now(a) | ||
//use NonEmptyList specific methods for creation and extraction | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe a good place to point out the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. //in order to have a lawful bimonad `pure` and `extract` need to respect: `nelBimonad.extract(nelBimonad.pure(a)) <-> a` Something like this ? |
||
override def pure[A](a: A): NonEmptyList[A] = | ||
NonEmptyList.one(a) | ||
|
||
override def extract[A](x: Eval[A]): A = | ||
x.value | ||
override def extract[A](fa: NonEmptyList[A]): A = | ||
fa.head | ||
|
||
//use the coflatMap from the Eval comonad | ||
override def coflatMap[A, B](fa: Eval[A])(f: Eval[A] => B): Eval[B] = | ||
//use the coflatMap from the NonEmptyList comonad | ||
override def coflatMap[A, B](fa: NonEmptyList[A])(f: NonEmptyList[A] => B): NonEmptyList[B] = | ||
comonad.coflatMap(fa)(f) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually we can directly call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was hopping this shows There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I do see that now. But then isn't it confusing, because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. nelBimonad.pure(true).extract === NonEmptyList.one(true).head There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I agree, I think this is the right way to do it. So, we can just delegate Anyway just my opinion, not really sure what the best thing to do here is. But I think separating the implementations of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So the current
Yes, I will remove |
||
|
||
//use the flatMap and tailRecM from the Eval monad | ||
override def flatMap[A, B](fa: Eval[A])(f: A => Eval[B]): Eval[B] = | ||
//use the flatMap and tailRecM from the NonEmptyList monad | ||
override def flatMap[A, B](fa: NonEmptyList[A])(f: A => NonEmptyList[B]): NonEmptyList[B] = | ||
monad.flatMap(fa)(f) | ||
|
||
override def tailRecM[A, B](a: A)(f: A => Eval[Either[A, B]]): Eval[B] = | ||
override def tailRecM[A, B](a: A)(f: A => NonEmptyList[Either[A, B]]): NonEmptyList[B] = | ||
monad.tailRecM(a)(f) | ||
} | ||
``` | ||
|
||
Note the equivalence: | ||
```scala mdoc | ||
evalBimonad.pure(true).extract === Eval.now(true).value | ||
nelBimonad.pure(true).extract === NonEmptyList.one(true).head | ||
``` | ||
|
||
Using generic bimonad syntax we could define a function that appends and extracts a configuration: | ||
|
@@ -70,14 +70,12 @@ def make[T[_]: Bimonad](config: T[String]): String = | |
.extract | ||
``` | ||
|
||
This will work with all types of `Eval`: | ||
This works with one element non-empty lists: | ||
```scala mdoc | ||
make(Eval.now("config")) | ||
|
||
make(Eval.later("config")) | ||
make(NonEmptyList.one("config")) | ||
``` | ||
|
||
`Function0` and `NonEmptyList` are also lawful bimonads so the following calls are also valid: | ||
`Function0[_]` and `Eval[_]` are also lawful bimonads so the following calls are also valid: | ||
```scala mdoc | ||
make(() => "config") | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.