What is Comonad, if it's possible describe in Scala syntax. I found scalaz library implementation, but it's not clear where it can be useful.
Comonad is a Functor and provides duals of the Monad 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, or even possible, depends on the particular type.
Comonads are functors The category theoretic notion of a functor can be. used to abstract map-like operations on parametric data types. In Haskell, func- tors are described by the Functor type class, of which map provides the list.
Well, monads allow you to add values to them, change them based on a computation from a non-monad to a monad. Comonads allow you to extract values from them, and change them based on a computation from a comonad to a non-comonad.
The natural intuition is that they'll usually appear where you have a CM[A] and want to extract A.
See this very interesting post that touches on comonads a bit casually, but, to me at least, making them very clear.
What follows is a literal translation of code from this blog post.
case class U[X](left: Stream[X], center: X, right: Stream[X]) {
def shiftRight = this match {
case U(a, b, c #:: cs) => U(b #:: a, c, cs)
}
def shiftLeft = this match {
case U(a #:: as, b, c) => U(as, a, b #:: c)
}
}
// Not necessary, as Comonad also has fmap.
/*
implicit object uFunctor extends Functor[U] {
def fmap[A, B](x: U[A], f: A => B): U[B] = U(x.left.map(f), f(x.center), x.right.map(f))
}
*/
implicit object uComonad extends Comonad[U] {
def copure[A](u: U[A]): A = u.center
def cojoin[A](a: U[A]): U[U[A]] = U(Stream.iterate(a)(_.shiftLeft).tail, a, Stream.iterate(a)(_.shiftRight).tail)
def fmap[A, B](x: U[A], f: A => B): U[B] = U(x.left.map(f), x.center |> f, x.right.map(f))
}
def rule(u: U[Boolean]) = u match {
case U(a #:: _, b, c #:: _) => !(a && b && !c || (a == b))
}
def shift[A](i: Int, u: U[A]) = {
Stream.iterate(u)(x => if (i < 0) x.shiftLeft else x.shiftRight).apply(i.abs)
}
def half[A](u: U[A]) = u match {
case U(_, b, c) => Stream(b) ++ c
}
def toList[A](i: Int, j: Int, u: U[A]) = half(shift(i, u)).take(j - i)
val u = U(Stream continually false, true, Stream continually false)
val s = Stream.iterate(u)(_ =>> rule)
val s0 = s.map(r => toList(-20, 20, r).map(x => if(x) '#' else ' '))
val s1 = s.map(r => toList(-20, 20, r).map(x => if(x) '#' else ' ').mkString("|")).take(20).force.mkString("\n")
println(s1)
Output:
| | | | | | | | | | | | | | | | | | | |#| | | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |#|#| | | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |#| |#| | | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |#|#|#|#| | | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |#| | | |#| | | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |#|#| | |#|#| | | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |#| |#| |#| |#| | | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |#|#|#|#|#|#|#|#| | | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |#| | | | | | | |#| | | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |#|#| | | | | | |#|#| | | | | | | | | |
| | | | | | | | | | | | | | | | | | | |#| |#| | | | | |#| |#| | | | | | | | |
| | | | | | | | | | | | | | | | | | | |#|#|#|#| | | | |#|#|#|#| | | | | | | |
| | | | | | | | | | | | | | | | | | | |#| | | |#| | | |#| | | |#| | | | | | |
| | | | | | | | | | | | | | | | | | | |#|#| | |#|#| | |#|#| | |#|#| | | | | |
| | | | | | | | | | | | | | | | | | | |#| |#| |#| |#| |#| |#| |#| |#| | | | |
| | | | | | | | | | | | | | | | | | | |#|#|#|#|#|#|#|#|#|#|#|#|#|#|#|#| | | |
| | | | | | | | | | | | | | | | | | | |#| | | | | | | | | | | | | | | |#| | |
| | | | | | | | | | | | | | | | | | | |#|#| | | | | | | | | | | | | | |#|#| |
| | | | | | | | | | | | | | | | | | | |#| |#| | | | | | | | | | | | | |#| |#|
| | | | | | | | | | | | | | | | | | | |#|#|#|#| | | | | | | | | | | | |#|#|#|#
The scalaz library provides a ComonadStore
which extends the property of Comonad
. It is defined like this:
trait ComonadStore[F[_], S] extends Comonad[F] { self =>
def pos[A](w: F[A]): S
def peek[A](s: S, w: F[A]): A
def peeks[A](s: S => S, w: F[A]): A =
peek(s(pos(w)), w)
def seek[A](s: S, w: F[A]): F[A] =
peek(s, cojoin(w))
def seeks[A](s: S => S, w: F[A]): F[A] =
peeks(s, cojoin(w))
def experiment[G[_], A](s: S => G[S], w: F[A])(implicit FG: Functor[G]): G[A] =
FG.map(s(pos(w)))(peek(_, w))
}
And a Store
( which is analogous to (S => A, S)
) has an instance of Comonad
. You can look at this question that explains what it is more specifically.
You also have the Coreader
and Cowriter
Comonads
that are the dual of the Reader
and Writer
Monads
, here is an excellent blog post that talks about it in Scala.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With