Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comonad example in Scala

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.

like image 702
Stas Avatar asked Jun 19 '12 20:06

Stas


People also ask

What is a Comonad?

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.

What is a Comonad Haskell?

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.


3 Answers

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.

like image 150
Daniel C. Sobral Avatar answered Oct 24 '22 06:10

Daniel C. Sobral


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:

 | | | | | | | | | | | | | | | | | | | |#| | | | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |#|#| | | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |#| |#| | | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |#|#|#|#| | | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |#| | | |#| | | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |#|#| | |#|#| | | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |#| |#| |#| |#| | | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |#|#|#|#|#|#|#|#| | | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |#| | | | | | | |#| | | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |#|#| | | | | | |#|#| | | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |#| |#| | | | | |#| |#| | | | | | | | |
 | | | | | | | | | | | | | | | | | | | |#|#|#|#| | | | |#|#|#|#| | | | | | | |
 | | | | | | | | | | | | | | | | | | | |#| | | |#| | | |#| | | |#| | | | | | |
 | | | | | | | | | | | | | | | | | | | |#|#| | |#|#| | |#|#| | |#|#| | | | | |
 | | | | | | | | | | | | | | | | | | | |#| |#| |#| |#| |#| |#| |#| |#| | | | |
 | | | | | | | | | | | | | | | | | | | |#|#|#|#|#|#|#|#|#|#|#|#|#|#|#|#| | | |
 | | | | | | | | | | | | | | | | | | | |#| | | | | | | | | | | | | | | |#| | |
 | | | | | | | | | | | | | | | | | | | |#|#| | | | | | | | | | | | | | |#|#| |
 | | | | | | | | | | | | | | | | | | | |#| |#| | | | | | | | | | | | | |#| |#|
 | | | | | | | | | | | | | | | | | | | |#|#|#|#| | | | | | | | | | | | |#|#|#|#
like image 41
missingfaktor Avatar answered Oct 24 '22 05:10

missingfaktor


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.

like image 1
Valy Dia Avatar answered Oct 24 '22 06:10

Valy Dia