Skip to content

Instantly share code, notes, and snippets.

@yujikiriki
Forked from jamesthompson/RedisFreeAlgebra
Last active August 29, 2015 14:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yujikiriki/59426c00c3fc1e4b681d to your computer and use it in GitHub Desktop.
Save yujikiriki/59426c00c3fc1e4b681d to your computer and use it in GitHub Desktop.
// 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