Skip to content

Instantly share code, notes, and snippets.

@benhutchison
Created February 3, 2017 21:48
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 benhutchison/b52260ae3e26e0e8e43b4f07e38d804a to your computer and use it in GitHub Desktop.
Save benhutchison/b52260ae3e26e0e8e43b4f07e38d804a to your computer and use it in GitHub Desktop.
Example: Change an Effect stack part way through a program
//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