Higher Order Effects
a.k.a Scoped Effects.
HOEs are problematic
- New programming languages with native Algebraic Effects, generally don’t support HOEs. Exceptions are:
-
According to the underlying theory, HOEs are actually non-algebraic ⚠️λ1.
-
The Eff Monad doesn’t support HOEs.
- Monad Transformers do support HOEs. However, there are some known problems. Such as effect’s semantics being dependent
on the order of monad transformers in the stack. More info on the subject:
- Unresolved challenges of scoped effects ⚠️λ1 video.
- Effect Semantics Zoo ⚠️λ1
HOEs in Turbolift
In this example, we run the given program twice, with 2 orderings of handlers:
Error
handled beforeState
.State
handled beforeError
.
We observe consistent behavior: in both cases,
raising the error didn’t cause the State
to reset to it’s value from
before the catchAll
operation.
import turbolift.!!
import turbolift.effects.{Error, State}
case object MyError extends Error[String]
case object MyState extends State[Int]
val program =
MyError.catchAll {
MyState.put(42) &&!
MyError.raise("error")
} {
case _ => !!.pure("nvm")
}
// program: Computation[Computation[String, Any], MyState & MyError] = turbolift.Computation@40fdaa70
val result1 = program
.handleWith(MyError.handler)
.handleWith(MyState.handler(0))
.run
// result1: Tuple2[Either[String, Computation[String, Any]], Int] = (
// Right(value = turbolift.Computation@27fceb25),
// 42
// )
val result2 = program
.handleWith(MyState.handler(0))
.handleWith(MyError.handler)
.run
// result2: Either[String, Tuple2[Computation[String, Any], Int]] = Right(
// value = (turbolift.Computation@12ece9d0, 42)
// )