The Either
effect is similar to the Option
effect but adds the possibility to specify why a computation
stopped:
import org.atnos.eff._, all._, syntax.all._
/**
* Stack declaration
*/
type S = Fx.fx1[Either[String, *]]
// compute with this stack
val map: Map[String, Int] =
Map("key1" -> 10, "key2" -> 20)
// get 2 keys from the map and add the corresponding values
def addKeys(key1: String, key2: String): Eff[S, Int] = for {
a <- optionEither(map.get(key1), s"'$key1' not found")
b <- optionEither(map.get(key2), s"'$key2' not found")
} yield a + b
(addKeys("key1", "key2").runEither.run, addKeys("key1", "missing").runEither.run)
> (Right(30),Left('missing' not found))
Note: the *
syntax comes from the kind-projector
project and allows us to avoid type lambdas.
A catchLeft
method can also be used to intercept an
error and possibly recover from it:
case class TooBig(value: Int)
type E = Fx.fx1[Either[TooBig, *]]
val i = 7
val value: Eff[E, Int] =
if (i > 5) left[E, TooBig, Int](TooBig(i))
else right[E, TooBig, Int](i)
val action: Eff[E, Int] = catchLeft[E, TooBig, Int](value) { case TooBig(k) =>
if (k < 10) right[E, TooBig, Int](k)
else left[E, TooBig, Int](TooBig(k))
}
action.runEither.run ==== Right(7)
Note: the type annotations on left
and
right
can be avoided by adding an implicit declaration in
scope. You can learn more about this in the
Implicits
section.