Created
January 19, 2015 22:00
-
-
Save jamesthompson/75fcef11d0aee6c9a72d to your computer and use it in GitHub Desktop.
An example toy algebra for Redis actions
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
// This should work with Scala 2.10.4 & scalaz 7.1, core, effect and concurrent packages | |
import scalaz.{ concurrent, Free, Functor, Monad, syntax } | |
import concurrent.Task | |
import Free.{freeMonad => _, _} | |
import syntax.monad._ | |
// Describe the set of actions - which are functors | |
sealed trait RedisF[+A] { | |
def map[B](fn: A => B): RedisF[B] | |
} | |
object RedisF { | |
implicit val redisFunctor: Functor[RedisF] = new Functor[RedisF] { | |
def map[A, B](fa: RedisF[A])(f: A => B): RedisF[B] = fa map f | |
} | |
} | |
// and some instances of that trait describing our desired actions | |
case class Get[+A](k: String, next: String => A) extends RedisF[A] { | |
def map[B](fn: A => B): RedisF[B] = copy(next = next andThen fn) | |
} | |
case class Set[+A](k: String, value: String, next: String => A) extends RedisF[A] { | |
def map[B](fn: A => B): RedisF[B] = copy(next = next andThen fn) | |
} | |
case class Fail[A](excp: Throwable) extends RedisF[A] { | |
def map[B](fn: A => B): RedisF[B] = Fail[B](excp) | |
} | |
// it's a simple dsl :-), it does getting and setting strings to Redis | |
object FreeRedis extends FreeRedis | |
trait FreeRedis { | |
type RedisFree[A] = Free[RedisF, A] | |
def just[A](a: => A): RedisFree[A] = | |
Monad[RedisFree].pure(a) | |
def noAction[A]: RedisFree[A] = | |
liftF(Fail(new Throwable("Action failed"))): RedisFree[A] | |
// Helper functions describing our redis commands below | |
def set(k: String, v: String): RedisFree[String] = | |
liftF(Set(k, v, identity)) | |
def get(k: String): RedisFree[String] = | |
liftF(Get(k, identity)) | |
implicit val redisFreeMonad: Monad[RedisFree] = new Monad[RedisFree] { | |
def point[A](a: => A) = Free.point(a) | |
def bind[A,B](action: RedisFree[A])(f: A => RedisFree[B]) = action flatMap f | |
} | |
} | |
// Ensure FreeRedis stuff is in scope... | |
import FreeRedis._ | |
// Make a pure program value | |
val program1: RedisFree[String] = set("keyA", "valueB") >> get("keyA") | |
object Interpreters extends FreeRedis { | |
// Take a program and turn each action into a string, aggregate those strings in a list | |
def stepList[A](program: RedisFree[A], actions: List[String] = Nil): List[String] = program.resume.fold({ | |
case Set(k, v, next) => stepList(next("success"), s"setting key: ${k} value = ${v}" :: actions) | |
case Get(k, next) => stepList(next("success"), s"getting key: ${k}" :: actions) | |
case Fail(excp) => ("ERROR!" :: actions).reverse | |
}, { a: A => ("End of program" :: actions).reverse }) | |
// Interpret a step (action) of our program and produce a Task which we can run later | |
def step[A](action: RedisF[RedisFree[A]]): Task[RedisFree[A]] = action match { | |
case Set(k, v, next) => Task { println(s"setting key: ${k} value = ${v}"); "success" } map { next } | |
case Get(k, next) => Task { println(s"getting key: ${k}"); "I'm the key you got!" } map { next } | |
case Fail(excp) => Task.fail(excp) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment