Skip to content

Instantly share code, notes, and snippets.

@conniec
Last active October 5, 2017 00:19
Show Gist options
  • Save conniec/c53b93127b606a8af39f to your computer and use it in GitHub Desktop.
Save conniec/c53b93127b606a8af39f to your computer and use it in GitHub Desktop.
Free Monad example with Cats and Scalaz
import cats.free.Free
import cats.~>
import cats._, cats.std.all._
import cats.Functor
// https://github.com/non/cats/blob/master/docs/src/main/tut/freemonad.md
sealed trait Command[+Next]
case class Person(name: String) extends AnyVal
case class Push[Next](person: Person, next: Next) extends Command[Next]
case class Pop[Next](onResult: Person => Next) extends Command[Next]
object CatsFreeMonadExample {
implicit val functor: Functor[Command] = new Functor[Command] {
def map[A, B](fa: Command[A])(f: A => B): Command[B] = fa match {
case Push(p, next) => Push(p, f(next))
case Pop(next) => {
def func(person: Person): B = f(next(person))
Pop(func)
}
}
}
def push(p: Person): Free[Command, Unit] = Free.liftF(Push(p, ()))
def pop: Free[Command, Person] = Free.liftF(Pop(identity))
val logic: Free[Command, Person] = for {
_ <- push(Person("Alice"))
_ <- push(Person("Bob"))
person <- pop
} yield person
var stack = Seq.empty[Person]
val invoker = new (Command ~> Id) {
override def apply[A](e: Command[A]): Id[A] = e match {
case Push(p, next) => {
stack = Seq(p) ++ stack
println(s"put $p")
next
}
case Pop(next) => {
val head = stack.head
println(s"pop $head")
stack = stack.tail
next(head)
}
}
}
// To run the logic with invoker
val result = logic.foldMap(invoker)
println(result)
}
import scalaz.Free
import scalaz._
import scalaz.syntax._
import scalaz.std.list._
import scalaz.syntax.traverse._
// http://eed3si9n.com/learning-scalaz/Free+Monad.html
sealed trait Command[+Next]
case class Person(name: String) extends AnyVal
case class Push[Next](person: Person, next: Next) extends Command[Next]
case class Pop[Next](onResult: Person => Next) extends Command[Next]
object ScalazFreeMonadExample {
implicit val commandFunctor: Functor[Command] = new Functor[Command] {
def map[A, B](fa: Command[A])(f: A => B): Command[B] = fa match {
case Push(p, next) => Push[B](p, f(next))
case Pop(next) => {
def func(person: Person): B = f(next(person))
Pop[B](func)
}
}
}
def push(p: Person): Free[Command, Unit] = Free.liftF(Push(p, ())) // Next is Unit
def pop: Free[Command, Person] = Free.liftF(Pop(identity)) // Next is Person
val logic: Free[Command, Person] = for {
_ <- push(Person("Alice"))
_ <- push(Person("Bob"))
person <- pop
} yield person
var stack = Seq.empty[Person]
def invoker[A](p: Free[Command, A]): A = {
p.resume.fold({
case Push(p, next) => {
stack = Seq(p) ++ stack
invoker(next)
}
case Pop(next) => {
val head = stack.head
println(s"pop $head")
stack = stack.tail
invoker(next(head))
}
},
{r: A => r})
}
// To run the logic with invoker
val result = invoker(logic)
println(result)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment