Created
February 3, 2017 21:48
-
-
Save benhutchison/b52260ae3e26e0e8e43b4f07e38d804a to your computer and use it in GitHub Desktop.
Example: Change an Effect stack part way through a program
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
//See this example at http://scastie.org/26026 | |
/*** | |
scalaVersion := "2.12.1" | |
libraryDependencies += "org.atnos" %% "eff" % "2.2.0" | |
addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.3") | |
scalacOptions += "-Ypartial-unification" | |
*/ | |
import cats._ | |
import cats.data._ | |
import cats.implicits._ | |
import org.atnos.eff._ | |
import org.atnos.eff.all._ | |
import org.atnos.eff.syntax.all._ | |
//This example shows how to vary an Eff stack midway through a program, so that a part of your system uses a more complex | |
//Effect stack, which gets interpreted away. | |
//The ability to vary and partially interpret the Effect stack is a key advantage of Eff over Monad Transformers | |
//However, its non-obvious what code idioms work best for varying the stack midway through a program, and there are plenty of | |
//forms that don't work well (eg type inference problems). This example distills some hard earned lessons from trial and error. | |
object ChangeEffectExample extends App { | |
type Stack = Fx.fx2[Either[String, ?], Writer[String, ?]] | |
type Err[R] = Either[String, ?] |= R | |
type Log[R] = Writer[String, ?] |= R | |
type Count[R] = State[Int, ?] |= R | |
type With[Effect[_], R] = Fx.prepend[Effect, R] | |
println(method[Stack](4).runWriter.runEither.run) | |
def method[R: Err: Log](n: Int): Eff[R, Int] = for { | |
//this call uses the stack R | |
_ <- assertErr(n >= 0, s"$n is negative") | |
//the call uses the stack R plus an additional State[Int, ?] which is interpreted here. The other effects remain | |
result <- subMethod[With[State[Int, ?], R]].runState(n) | |
// pattern matching tuples needs to be done on a following line due to rather obscure limitations of Scala | |
// for more details why: http://stackoverflow.com/questions/37982029/how-to-annotate-types-of-expressions-within-a-scala-for-expression | |
(_, n2) = result | |
} yield n2 | |
def assertErr[R: Err](expr: Boolean, msg: String): Eff[R, Unit] = | |
if (!expr) | |
left(msg) | |
else | |
right(()) | |
def subMethod[R: Err: Log: Count]: Eff[R, Unit] = for { | |
c <- get | |
c2 = c * c | |
_ <- tell(s"changing the count value to $c2") | |
_ <- put(c2) | |
} yield (()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment