Last active
August 29, 2015 13:57
-
-
Save danclien/9800403 to your computer and use it in GitHub Desktop.
Attempt to write a monad for console IO using scalaz's Free monad
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
:paste | |
import scalaz._, Scalaz._, scalaz.Free.{Suspend, Return} | |
// Console grammar | |
sealed trait ConsoleF[+A] | |
object Console { | |
case class WriteLine[A](msg: String, o: A) extends ConsoleF[A] | |
case class ReadLine[A](o: String => A) extends ConsoleF[A] | |
} | |
// Implement the Functor typeclass for ConsoleF with implicit conversion | |
implicit def consoleFFunctor[B]: Functor[ConsoleF] = new Functor[ConsoleF]{ | |
import Console._ | |
def map[A,B](fa: ConsoleF[A])(f: A => B): ConsoleF[B] = | |
fa match { | |
case ReadLine(h) => ReadLine(x => f(h(x))) | |
case WriteLine(msg, a) => WriteLine(msg, f(a)) | |
} | |
} | |
// Implicit conversion from ConsoleF to the Free monad | |
implicit def consoleFToFree[A](consolef: ConsoleF[A]): Free[ConsoleF,A] = | |
Suspend[ConsoleF, A](Functor[ConsoleF].map(consolef)(a => Return[ConsoleF, A](a))) | |
// Type aliasing | |
type Console[A] = Free[ConsoleF, A] | |
// Helper methods for the for-comprehension | |
object console { | |
import Console._ | |
def writeLn(msg: String): Free[ConsoleF, Unit] = WriteLine(msg, ()) | |
def readLn(): Free[ConsoleF, String] = ReadLine(v => v) | |
} | |
// Create program as data | |
val program = for { | |
_ <- console.writeLn("Type in a sentence to echo and hit enter.") | |
v <- console.readLn() | |
_ <- console.writeLn("Echoing: " + v) | |
} yield () | |
// Interpreter with side-effects | |
object ConsoleInterpreter { | |
import Console._ | |
import scalaz.{~>,Id}, Id.Id | |
private val exe: ConsoleF ~> Id = new (ConsoleF ~> Id) { | |
def apply[B](l: ConsoleF[B]): B = l match { | |
case WriteLine(msg, a) => { println(msg); a } | |
case ReadLine(h) => { h(readLine()) } | |
} | |
} | |
def apply[A](c: Console[A]): A = | |
c.runM(exe.apply[Console[A]]) | |
} | |
// Ctrl-D to exit paste mode | |
// Run the program | |
ConsoleInterpreter(program) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment