Skip to content

Instantly share code, notes, and snippets.

@sscarduzio
Created May 15, 2016 22:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sscarduzio/c80da3d89e9c33f2e01c99d0423db3a4 to your computer and use it in GitHub Desktop.
Save sscarduzio/c80da3d89e9c33f2e01c99d0423db3a4 to your computer and use it in GitHub Desktop.
Cats Free Monad. One algebra, many interpreters.
/**
* 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