Last active
December 11, 2015 21:09
-
-
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.
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
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