I have example (below) of a Free language along with its dual. I was looking into using the zapWith functionality on the Free type to produce a result. However, I am a bit stumped as to why the call to zapWith (below) yields a stack overflow instead of the desired result. There must be something I a missing here, any pointers would be most appreciated.
Also, I am curious whether the usage of Cofree in the example below is on the mark or not. What I mean is that I would like to perhaps yield an optional value when I access the id from the DB type. For example, I am not sure I understand how this might work if I wanted to call `db.get(id)` as opposed to just `db(id)`.
import scalaz.{-\/, \/-, \/, Cofree, Free, Functor, Zap}
import Free.{Gosub, Return, Suspend}
object tester {
type Foo = String
sealed trait FooLanguage[+A]
case class GetFoo[+A](id: String, h: Foo => A) extends FooLanguage[A]
sealed trait FooLanguageDual[+A]
case class GetFooDual[+A](a: A, k: String => Foo) extends FooLanguageDual[A]
implicit val fooLanguageFunctor: Functor[FooLanguage] =
new Functor[FooLanguage] {
def map[A, B](a: FooLanguage[A])(f: A => B): FooLanguage[B] = a match {
case GetFoo(i, h) => GetFoo(i, x => f(h(x)))
}
}
implicit val fooLanguageDualFunctor: Functor[FooLanguageDual] =
new Functor[FooLanguageDual] {
def map[A, B](a: FooLanguageDual[A])(f: A => B): FooLanguageDual[B] = a match {
case GetFooDual(a, k) => GetFooDual(f(a), k)
}
}
implicit val fooLanguageDualZap: Zap[FooLanguage, FooLanguageDual] =
new Zap[FooLanguage, FooLanguageDual] {
def zapWith[A, B, C](fa: FooLanguage[A], gb: FooLanguageDual[B])(f: (A, B) => C) = (fa, gb) match {
case (GetFoo(i, h), GetFooDual(b, k)) => f(h(k(i)), b)
}
}
def getFoo(id: String): Free[FooLanguage, Foo] =
Suspend[FooLanguage, Foo](GetFoo(id, Return[FooLanguage, Foo](_)))
type DB = Map[String, Foo]
val db: DB = Map("test" -> "foo")
def run(db: DB): Cofree[FooLanguageDual, DB] =
Cofree.unfoldC[FooLanguageDual, DB](db) { db =>
GetFooDual(db, db(_))
}
val program =
for {
x <- getFoo("test")
} yield s"howdy $x"
// Stackoverflows
lazy val res: String = program.zapWith(run(db))((a, _) => a)
def run2[A](p: Free[FooLanguage, A], db: DB): A =
p.resume.fold({
case GetFoo(i, h) => run2(h(db(i)), db)
}, a => a)
// Produces "howdy foo"
lazy val res2: String = run2(program, db)
}