Skip to content

Instantly share code, notes, and snippets.

@monaddle
Created September 9, 2017 03:00
Show Gist options
  • Save monaddle/ddfd8374e03fca9b77f7b6c3353f296d to your computer and use it in GitHub Desktop.
Save monaddle/ddfd8374e03fca9b77f7b6c3353f296d to your computer and use it in GitHub Desktop.
free monads using scalaz. Example of composing interpreters.
package testfree
import testfree.Console._
import testfree.Authorization.{ Authorize}
import scalaz.{Inject, _}
import Scalaz._
import scala.io.StdIn
import scalaz.Inject._
// Example of using two separate ADTs with separate interpreters and composing the interpeters.
// See talk by Runar Bjarnason https://www.youtube.com/watch?v=M258zVn4m2M, or "onion architecture" description
// by de Goes - http://degoes.net/articles/modern-fp-part-2
//
// I'm doing something slightly to create the coproduct of the interpreters. In Bjarnason's talk they're using cats. I tried to
// inject the "or" function with implicits but was getting some arcane compile error.
trait Console[A]
object Console {
case class Ask(msg: String) extends Console[String]
case class Tell(msg: String) extends Console[Unit]
}
class Auths[F[_]](implicit i: Inject[Authorization, F]) {
def authorize(secret: String): Free[F, Boolean] = Free.liftF(i.inj(Authorize(secret)))
}
class Interacts[F[_]](implicit i: Inject[Console, F]) {
def tell(msg: String): Free[F, Unit] = Free.liftF(i.inj(Tell(msg)))
def ask(msg: String): Free[F, String] = Free.liftF(i.inj(Ask(msg)))
}
trait Authorization[A]
object Authorization {
case class Authorize(secret: String) extends Authorization[Boolean]
}
object Program {
type copr[A] = Coproduct[Console, Authorization, A]
object run extends (Console ~> Id ) {
def apply[A](console: Console[A]): Id[A] = {
console match {
case Ask(msg)
=> println(msg)
StdIn.readLine()
case Tell(msg)
=> println(msg)
()
}
}
}
object MyAuth extends (Authorization ~> Id) {
def apply[A](auth: Authorization[A]): Id[A] = {
auth match {case Authorize(x) => true}
}
}
def or[F[_], G[_], H[_]](fg: F~>G, hg: H ~> G) = new (({type t[x] = Coproduct[F, H, x]})#t ~> G) {
def apply[A](fa: Coproduct[F, H, A]): G[A] = {
fa.run match {
case \/-(x) => hg(x)
case -\/(x) => fg(x)
}
}
}
def main(args: Array[String]) {
implicit val interacts = new Interacts[copr]
implicit val auths = new Auths[copr]
def prg[F[_]](implicit I: Interacts[F], A: Auths[F]): Free[F, Unit] = {
import I._; import A._;
for {
id <- ask("what is your name?")
success <- authorize(id)
_ <- tell(s"$id, huh. it's $success, that's very interesting")
} yield ()
}
val pg = prg[copr]
pg.foldMap(or(run, MyAuth))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment