Skip to content

Instantly share code, notes, and snippets.

@chenharryhua
Last active March 15, 2018 03:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chenharryhua/a4d14896b2a26b30fe85e8d2cac143b1 to your computer and use it in GitHub Desktop.
Save chenharryhua/a4d14896b2a26b30fe85e8d2cac143b1 to your computer and use it in GitHub Desktop.
type checked. but runtime error: java.lang.ClassCastException: scala.runtime.BoxedUnit cannot be cast to scalaz.Free
package cofreecomonad
import scalaz._, Scalaz._
//import cats._, cats.data._, cats.implicits._
//import cats.free._
object ScalazVersion {
object free { // our dsl
sealed trait TerminalF[A] // an f-algebra which is a sum type
type Terminal[A] = Free[TerminalF, A] //free
final case class Print(a: Int) extends TerminalF[Unit]
final case class Read() extends TerminalF[Int]
implicit val terminalFunctor: Functor[TerminalF] = new Functor[TerminalF] { //sadly, can't derive functor
def map[A, B](fa: TerminalF[A])(f: A => B): TerminalF[B] = fa.asInstanceOf[TerminalF[B]]
}
// smart constructors
def print(a: Int): Terminal[Unit] = Free.liftF(Print(a))
def read(): Terminal[Int] = Free.liftF(Read())
}
object cofree { //our interpreter
trait CoTerminalF[A] { //product type. A is a continuation
def coPrint(i: Int): A
def coRead: (Int, A)
}
implicit object coterminalFunctor extends Functor[CoTerminalF] {
def map[A, B](fa: CoTerminalF[A])(f: A => B): CoTerminalF[B] = new CoTerminalF[B] {
def coPrint(i: Int): B = f(fa.coPrint(i))
def coRead: (Int, B) = {
val k = fa.coRead
(k._1, f(k._2))
}
}
}
type CoTerminal[A] = Cofree[CoTerminalF, A] //cofree
}
implicit object coterminal_zap_terminal extends Zap[cofree.CoTerminalF, free.TerminalF] {
override def zapWith[A, B, C](fa: cofree.CoTerminalF[A], gb: free.TerminalF[B])(f: (A, B) => C): C = {
gb match {
case free.Print(a) => f(fa.coPrint(a), ())
case free.Read() => f.tupled(fa.coRead.swap)
}
}
}
}
object test extends App {
import ScalazVersion.free._
import ScalazVersion.cofree._
def interp: CoTerminal[Unit] =
Cofree.unfoldC[CoTerminalF, Unit](()) { _ =>
new CoTerminalF[Unit] {
def coPrint(i: Int): Unit = (println(s"coprint $i"))
def coRead: (Int, Unit) = { (10, println("coread")) }
}
}
val program: Terminal[Int] = for {
_ <- print(10)
a <- read()
b <- read()
_ <- print(a + b)
} yield b
interp.zapWith(program) { (a, b) => println("done") }
}
@chenharryhua
Copy link
Author

chenharryhua commented Mar 14, 2018

use non-GADT encoding. it works.

package cofreecomonad
import scalaz._, Scalaz._
//import cats._, cats.data._, cats.implicits._
//import cats.free._

object ScalazVersion {
  object free { // our dsl
    sealed trait TerminalF[A] // an f-algebra which is a sum type
    type Terminal[A] = Free[TerminalF, A] //free
    final case class Print[A](a: Int, k: A) extends TerminalF[A]
    final case class Read[A](k: Int => A) extends TerminalF[A]

    implicit val terminalFunctor: Functor[TerminalF] = new Functor[TerminalF] { //sadly, can't derive functor automatically
      def map[A, B](fa: TerminalF[A])(f: A => B): TerminalF[B] = fa match {
        case Print(a, k) => Print(a, f(k))
        case Read(k)     => Read(a => f(k(a)))
      }
    }
    // smart constructors
    def print(a: Int): Terminal[Unit] = Free.liftF(Print(a, ()))
    def read(): Terminal[Int] = Free.liftF(Read(identity))
  }

  object cofree { //our interpreter
    trait CoTerminalF[A] { //a product type. A is a continuation
      def coPrint(i: Int): A
      def coRead: (Int, A)
    }
    implicit object coterminalFunctor extends Functor[CoTerminalF] {
      def map[A, B](fa: CoTerminalF[A])(f: A => B): CoTerminalF[B] = new CoTerminalF[B] {
        def coPrint(i: Int): B = f(fa.coPrint(i))
        def coRead: (Int, B) = {
          val k = fa.coRead
          (k._1, f(k._2))
        }
      }
    }
    type CoTerminal[A] = Cofree[CoTerminalF, A] //cofree
  }

  implicit object coterminal_zap_terminal extends Zap[cofree.CoTerminalF, free.TerminalF] {
    override def zapWith[A, B, C](fa: cofree.CoTerminalF[A], gb: free.TerminalF[B])(f: (A, B) => C): C = {
      gb match {
        case free.Print(a, k) => f(fa.coPrint(a), k)
        case free.Read(k) =>
          val g = fa.coRead
          f(g._2, k(g._1))
      }
    }
  }
}

object test extends App {
  import ScalazVersion.free._
  import ScalazVersion.cofree._

  def interp: CoTerminal[Unit] =
    Cofree.unfoldC[CoTerminalF, Unit](()) { _ =>
      new CoTerminalF[Unit] {
        def coPrint(i: Int): Unit = (println(s"coprint $i"))
        def coRead: (Int, Unit) = { (10, println("coread")) }
      }
    }

  val program: Terminal[Int] = for {
    _ <- print(10)
    a <- read()
    b <- read()
    _ <- print(a + b)
  } yield b

  interp.zapWith(program) { (a, b) => println((a, b)) }
}

@chenharryhua
Copy link
Author

chenharryhua commented Mar 15, 2018

Besides,
TerminalF:

    final case class Print[A](a: Int, k: A) extends TerminalF[A]

looks like adjunct to

      def coPrint(i: Int): A

in a sense of that Print which is type of (Int, ?) and CoPrint which is (Int => ?) form an adjunction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment