diff --git a/docs/src/main/resources/microsite/data/menu.yml b/docs/src/main/resources/microsite/data/menu.yml index efb198d8b4..82857bb783 100644 --- a/docs/src/main/resources/microsite/data/menu.yml +++ b/docs/src/main/resources/microsite/data/menu.yml @@ -44,6 +44,11 @@ options: menu_type: typeclasses menu_section: monads + - title: Comonads + url: typeclasses/comonad.html + menu_type: typeclasses + menu_section: comonads + - title: Variance and Functors url: typeclasses/functor.html menu_type: typeclasses diff --git a/docs/src/main/tut/typeclasses/comonad.md b/docs/src/main/tut/typeclasses/comonad.md new file mode 100644 index 0000000000..09ca147600 --- /dev/null +++ b/docs/src/main/tut/typeclasses/comonad.md @@ -0,0 +1,85 @@ +--- +layout: docs +title: "Comonad" +section: "typeclasses" +source: "core/src/main/scala/cats/Comonad.scala" +scaladoc: "#cats.Comonad" +--- +# Comonad + +`Comonad` is a `Functor` and provides duals of the [`Monad`](monad.html) `pure` +and `flatMap` functions. A dual to a function has the same types but the +direction of the arrows are reversed. Whether or not that is useful even possible +depends on the particular type. For a more formal definition of duality, please +refer to [https://ncatlab.org/nlab/show/duality](https://ncatlab.org/nlab/show/duality). + +### extract + +Monad's have `pure` from `Applicative` which gives you the ability to wrap +a value `A` using the type constructor giving an `F[A]`. Comonad has +`extract` which instead takes an `F[A]` and extracts the `A`. Therefore, to be +able to implement extract we must have a type of which we are certain +we can get an `A` from an `F[A]`. For example we cannot always get an `A` +from a `List[A]` because if the list is empty there is nothing to get. + +For the same reason, `Option` doesn't have a Comand instance, because we +cannot always get an `A` from an Option, it may be empty too. + +Some examples that we can implement `Comonad` for include `OneAnd`, `Tuple2` +and the "non empty" collections. + +First some imports. + +```tut:silent +import cats._ +import cats.data._ +import cats.implicits._ +import cats.syntax.comonad._ +import cats.instances.list._ +``` + +`NonEmptyList` has a `Comonad` instance and its implementation of `extract` +simply returns the head element of the list, which we know we will always +have. + +```tut:book +NonEmptyList.of(1,2,3).extract +``` + +### coflatMap + +`coflatMap` is the dual of Monad's `flatMap`. While `flatMap` allows us to chain +together operations in a monadic context, `coflatMap` takes a value in some context + `F[A]` and a function `F[A] => B` and returns a new value in a context `F[B]`. + +The default implementation of `coflatMap` for `NonEmptyList` will pass the supplied +function with the whole list, then the tail of that, then the tail of that and so +on. This is illustrated below. + +```tut:book +NonEmptyList.of(1,2,3,4,5).coflatMap(identity) +``` + +# CoflatMap + +While `FlatMap` is a weaker version of `Monad` that doesn't have the `pure` function, +`CoflatMap` is a `Comonad` without the `extract` function. There are many instances +of type classes in Cats that implement `CoflatMap` but not `Comonad`. + +For example we cannot write `extract` for `Option[A]` because there's no way to +pull an `A` out of nowhere if the Option is empty. + +```tut:silent +def extract[A](fa : Option[A]): A = fa match { + case Some(a) => a + case None => ??? // What now? +} +``` + +Another example is `List`. Remember we cannot write `extract` for list because lists +can be empty, but we can implement the `coflatMap` and it works identically to the +one shown above for `NonEmptyList`. + +```tut:book +List(1,2,3,4,5).coflatMap(identity) +```