Created
September 9, 2017 03:00
-
-
Save monaddle/ddfd8374e03fca9b77f7b6c3353f296d to your computer and use it in GitHub Desktop.
free monads using scalaz. Example of composing interpreters.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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