Skip to content

Instantly share code, notes, and snippets.

@tpolecat
Last active December 11, 2015 21:09
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 tpolecat/4660668 to your computer and use it in GitHub Desktop.
Save tpolecat/4660668 to your computer and use it in GitHub Desktop.
A way to construct effect environments with private state (potentially impure) and primitive operations.
import scalaz._
import scalaz.Free._
import scalaz.std.function._
// IO-like effect world; like State[S,A] where S is private to the implementation
trait Effects[World] {
final class Action[+A] private[Effects] (private val t: World => Trampoline[(World, A)]) extends (World => A) {
def apply(w: World): A = run(w)._2
def run(w: World): (World, A) = t(w).run
def map[B](f: A => B): Action[B] = new Action(w => for { (nw, a) <- t(w) } yield (nw, f(a)))
def flatMap[B](f: A => Action[B]): Action[B] = new Action(w => for { (nw, a) <- t(w); x <- f(a).t(w) } yield x)
}
// Actions can only be constructed by subclasses (not happy with the names)
protected def action[A](f: World => (World, A)): Action[A] = new Action(w => return_(f(w)))
protected def effect[A](f: World => A): Action[A] = action(w => (w, f(w)))
protected def unit[A](a: => A): Action[A] = effect(_ => a)
// Semigroup, Monoid, Monad instances skipped
}
// Console effects for any world
trait ConsoleEffects { this: Effects[_] =>
def putStrLn(s: String) = unit(println(s))
}
// Effects for a mutable map world
trait MapEffects[K, V] { this: Effects[collection.mutable.Map[K, V]] =>
def put(k: K, v: V) = effect(_.put(k, v))
def get(k: K) = effect(_.get(k))
}
object Test extends App {
// An effect world for a mutable map with console IO
object MapWorld extends Effects[collection.mutable.Map[String, Int]]
with MapEffects[String, Int]
with ConsoleEffects
import MapWorld._
// A test action; no way to get a handle to the map
val action = for {
a <- get("foo")
_ <- putStrLn("a is " + a)
_ <- put("foo", 3)
b <- get("foo")
_ <- putStrLn("b is " + b)
c <- get("bar")
} yield (a, b, c)
val result = action.run(collection.mutable.HashMap("bar" -> 10)) // UNSAFE
println(result)
// a is None
// b is Some(3)
// (Map(foo -> 3, bar -> 10),(None,Some(3),Some(10)))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment