Created
May 15, 2016 22:29
-
-
Save sscarduzio/c80da3d89e9c33f2e01c99d0423db3a4 to your computer and use it in GitHub Desktop.
Cats Free Monad. One algebra, many interpreters.
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
/** | |
* Created by sscarduzio on 15/05/2016. | |
*/ | |
package free | |
object Main extends App { | |
// Algebra | |
sealed trait KVStoreA[A] | |
case class Put[T](key: String, value: T) extends KVStoreA[Unit] | |
case class Get[T](key: String) extends KVStoreA[Option[T]] | |
case class Delete(key: String) extends KVStoreA[Unit] | |
// Free the algebra | |
import cats.free.Free | |
type KVStore[A] = Free[KVStoreA, A] | |
import cats.free.Free.liftF | |
// Put returns nothing (i.e. Unit). | |
def put[T](key: String, value: T): KVStore[Unit] = | |
liftF[KVStoreA, Unit](Put[T](key, value)) | |
// Get returns a T value. | |
def get[T](key: String): KVStore[Option[T]] = | |
liftF[KVStoreA, Option[T]](Get[T](key)) | |
// Delete returns nothing (i.e. Unit). | |
def delete(key: String): KVStore[Unit] = | |
liftF(Delete(key)) | |
// Update composes get and set, and returns nothing. | |
def update[T](key: String, f: T => T): KVStore[Unit] = | |
for { | |
vMaybe <- get[T](key) | |
_ <- vMaybe.map(v => put[T](key, f(v))).getOrElse(Free.pure(())) | |
} yield () | |
// The DSL program | |
def program: KVStore[Option[Int]] = | |
for { | |
_ <- put("wild-cats", 2) | |
_ <- update[Int]("wild-cats", (_ + 12)) | |
_ <- put("tame-cats", new Exception) | |
n <- get[Int]("wild-cats") | |
_ <- delete("tame-cats") | |
} yield n | |
// An Interpreter | |
import cats.{Id, ~>} | |
import scala.collection.mutable | |
// the program will crash if a key is not found, | |
// or if a type is incorrectly specified. | |
def impureCompiler: KVStoreA ~> Id = | |
new (KVStoreA ~> Id) { | |
// a very simple (and imprecise) key-value store | |
val kvs = mutable.Map.empty[String, Any] | |
def apply[A](fa: KVStoreA[A]): Id[A] = | |
fa match { | |
case Put(key, value) => | |
println(s"put($key, $value)") | |
kvs(key) = value | |
() | |
case Get(key) => | |
println(s"get($key)") | |
kvs.get(key).map(_.asInstanceOf[A]) | |
case Delete(key) => | |
println(s"delete($key)") | |
kvs.remove(key) | |
() | |
} | |
} | |
import cats.std.all._ | |
def impureCompilerOption: KVStoreA ~> Option = | |
new (KVStoreA ~> Option) { | |
// a very simple (and imprecise) key-value store | |
val kvs = mutable.Map.empty[String, Any] | |
def apply[A](fa: KVStoreA[A]): Option[A] = | |
fa match { | |
case Put(key, value) => | |
println(s"put($key, $value)") | |
kvs(key) = value | |
Some(()) | |
case Get(key) => | |
println(s"get($key)") | |
Some(Option(kvs.get(key).map(_.asInstanceOf[A]))) | |
case Delete(key) => | |
println(s"delete($key)") | |
kvs.remove(key) | |
Some(()) | |
} | |
} | |
import cats.data._ | |
type KVStoreState[A] = State[Map[String, Any], A] | |
val pureCompiler: KVStoreA ~> KVStoreState = new (KVStoreA ~> KVStoreState) { | |
def apply[A](fa: KVStoreA[A]): KVStoreState[A] = | |
fa match { | |
case Put(key, value) => State.modify(_.updated(key, value)) | |
case Get(key) => | |
State.inspect(_.get(key).map(_.asInstanceOf[A])) | |
case Delete(key) => State.modify(_ - key) | |
} | |
} | |
// Running the DSL program with an interpreter | |
val res = program.foldMap(impureCompiler) | |
println(res) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment