Last active
November 6, 2015 15:53
-
-
Save msiegenthaler/a5d8a2db7cedd0fd6b93 to your computer and use it in GitHub Desktop.
Example for a free monad based console IO with cats. Based upon http://underscore.io/blog/posts/2015/04/14/free-monads-are-simple.html. See also https://gist.github.com/msiegenthaler/bb00ac37a5057fe2c7c9.
This file contains 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 example2 | |
import cats.std.list._ | |
import cats.std.function._ | |
import cats.syntax.foldable._ | |
import cats.state.State | |
import cats._ | |
import cats.free._ | |
object Console { | |
import Op._ | |
type Console[A] = Free[ConsoleOp, A] | |
implicit val monad = Free.freeMonad[ConsoleOp] | |
def readln: Console[String] = Free.liftF(Readln()) | |
def println(text: String): Console[Unit] = Free.liftF(Println(text)) | |
object Op { | |
sealed trait ConsoleOp[+A] | |
case class Println(text: String) extends ConsoleOp[Unit] | |
case class Readln() extends ConsoleOp[String] | |
} | |
} | |
/** Interpreter that reads from sysin and writes to sysout directly (side-effect). */ | |
object ConsoleInterpreterSysout { | |
import Console.Console, Console.Op._ | |
def apply[A](f: Console[A]) = f.foldMap(Compiler) | |
object Compiler extends (ConsoleOp ~> Id) { | |
override def apply[A](fa: ConsoleOp[A]): Id[A] = fa match { | |
case Println(text) ⇒ scala.Console.println(text) | |
case Readln() ⇒ scala.io.StdIn.readLine() | |
} | |
} | |
} | |
/** Interpreter that writes outputs to a list and takes input from another list. */ | |
object ConsoleInterpreterLists { | |
import Console.Console, Console.Op._ | |
def apply[A](f: Console[A]): ListState[A] = f.foldMap(Compiler) | |
type Lists = (List[String], List[String]) | |
type ListState[A] = State[Lists, A] | |
object Compiler extends (ConsoleOp ~> ListState) { | |
override def apply[A](fa: ConsoleOp[A]): ListState[A] = fa match { | |
case Println(text) ⇒ | |
for { | |
v ← State.get[Lists] | |
(ins, outs) = v | |
_ ← State.set((ins, outs :+ text)) | |
} yield () | |
case Readln() ⇒ | |
for { | |
v ← State.get[Lists] | |
(ins, outs) = v | |
_ ← State.set((ins.tail, outs)) | |
} yield (ins.head) | |
} | |
} | |
} | |
// Example use of the Console2 free monad | |
object Example { | |
import Console._ | |
import scala.language.higherKinds | |
val program: Console[String] = { | |
for { | |
_ <- println("Please tell me your name (empty to exit):") | |
greeting = "Hello" | |
name <- readln | |
_ <- println(s"$greeting $name") | |
} yield name | |
} | |
def repeat[M[_] : Monad](times: Int)(fa: M[_]) = List.fill(times)(fa).sequence_ | |
def iterateUntil[M[_] : Monad, A](pred: A ⇒ Boolean)(fa: M[A]): M[A] = | |
Monad[M].flatMap(fa)(y ⇒ if (pred(y)) Monad[M].pure(y) else iterateUntil(pred)(fa)) | |
def main(args: Array[String]): Unit = { | |
val res2 = ConsoleInterpreterLists(repeat(2)(program)). | |
run(("Maya" :: "Mario" :: Nil, Nil)).run | |
scala.Console.println(s"State results in ${res2._2} (outputs = ${res2._1._2})") | |
val res = ConsoleInterpreterSysout(iterateUntil((v: String) ⇒ v.isEmpty)(program)) | |
scala.Console.println(s"Sysout results in $res") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment