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:
    • Frank language.
    • Unison language, which implements Frank’s effect system.
  • 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:

HOEs in Turbolift

In this example, we run the given program twice, with 2 orderings of handlers:

  1. Error handled before State.
  2. State handled before Error.

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)
// )

  1. Warning: Haskell code ahead.  2 3