-
-
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 10 commits
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 |
---|---|---|
@@ -0,0 +1,85 @@ | ||
--- | ||
layout: docs | ||
title: "Bimonad" | ||
section: "typeclasses" | ||
source: "core/src/main/scala/cats/Bimonad.scala" | ||
scaladoc: "#cats.Bimonad" | ||
--- | ||
# Bimonad | ||
|
||
The `Bimonad` trait directly extends `Monad` and `Comonad` without introducing new methods. `Bimonad` is | ||
different from other `Bi` typeclasses like `Bifunctor`, `Bifoldable` or `Bitraverse` where the prefix describes | ||
a `F[_, _]`. The `Bimonad` is a `F[_]` and the `Bi` prefix has a different meaning here: it's both a `Monad` and a `Comonad`. | ||
Keep in mind `Bimonad` has its own added laws so something that is both monadic | ||
and comonadic may not necessarily be a lawful `Bimonad`. | ||
|
||
If you use `Bimonad` as a convenience type such that: | ||
```scala | ||
def f[T[_] : Monad, Comonad, S](fa: T[S]): S | ||
``` | ||
is re-written to: | ||
```scala | ||
def f[T[_] : Bimonad, S](fa: T[S]): S | ||
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. nit: we generally don't put a space after the type, so we would change to: |
||
``` | ||
then `T[_]` also needs to respect an extra set of laws. | ||
|
||
### 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: | ||
```scala mdoc | ||
import cats._ | ||
import cats.data._ | ||
import cats.implicits._ | ||
|
||
implicit def nelBimonad = | ||
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 would make this a |
||
new Bimonad[NonEmptyList] { | ||
|
||
// in order to have a lawful bimonad `pure` and `extract` need to respect: `nelBimonad.extract(nelBimonad.pure(a)) <-> a` | ||
override def pure[A](a: A): NonEmptyList[A] = | ||
NonEmptyList.one(a) | ||
|
||
override def extract[A](fa: NonEmptyList[A]): A = | ||
fa.head | ||
|
||
// use coflatMap from NonEmptyList | ||
override def coflatMap[A, B](fa: NonEmptyList[A])(f: NonEmptyList[A] => B): NonEmptyList[B] = | ||
fa.coflatMap(f) | ||
|
||
// use flatMap from NonEmptyList | ||
override def flatMap[A, B](fa: NonEmptyList[A])(f: A => NonEmptyList[B]): NonEmptyList[B] = | ||
fa.flatMap(f) | ||
|
||
// the tailRecM implementation is not the subject of this material | ||
// as an exercise try to implement it yourself | ||
override def tailRecM[A, B](a: A)(fn: A => NonEmptyList[Either[A, B]]): NonEmptyList[B] = | ||
??? | ||
} | ||
``` | ||
|
||
Note the equivalence: | ||
```scala mdoc | ||
nelBimonad.pure(true).extract === NonEmptyList.one(true).head | ||
``` | ||
|
||
Using generic bimonad syntax we could define a function that appends and extracts a configuration: | ||
```scala mdoc | ||
def make[T[_]: Bimonad](config: T[String]): String = | ||
config | ||
.flatMap(c => Bimonad[T].pure(c + " with option A")) | ||
.flatMap(c => Bimonad[T].pure(c + " with option B")) | ||
.flatMap(c => Bimonad[T].pure(c + " with option C")) | ||
.extract | ||
``` | ||
|
||
This works with one element non-empty lists: | ||
```scala mdoc | ||
make(NonEmptyList.one("config")) | ||
``` | ||
|
||
`Function0[_]` and `Eval[_]` are also lawful bimonads so the following calls are also valid: | ||
```scala mdoc | ||
make(() => "config") | ||
|
||
make(Eval.later("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.
I think this should be
def f[T[_]: Monad: Comonad, S](fa: T[S]): S
I think the comma here is making
Comonad
just a type parameter.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.
Yep my bad ..
:Monad :Comonad
is what I wanted