MonadDefer is a typeclass to abstract over computations that cause side effects. This means that the computations are deferred until they’re are asked to be performed synchronously. Without effect suspension, the effects would otherwise run immediately.

val now = IO.applicative().just(println("eager side effect"))
// Print: "eager side effect"

now.unsafeRunAsync { }
// Nothing, the effect has run already

val later = IO.monadDefer().invoke { println("lazy side effect") }
// Nothing, the effect is deferred until executed

later.unsafeRunAsync { }
// Print: "lazy side effect"

Main Combinators

All the new combinators added by MonadDefer are constructors. MonadDefer also includes all combinators present in MonadError.


Receives a function returning A. The instance is responsible for evaluating the function lazily.

IO.monadDefer().invoke { 1 }

As it captures exceptions, invoke() is the simplest way of wrapping existing synchronous APIs.

fun <F> getSongUrlAsync(SC: MonadDefer<F>) =
  SC { getSongUrl() }

val songIO: IOOf<Url> = getSongUrlAsync(IO.monadDefer())
val songDeferred: DeferredKOf<Url> = getSongUrlAsync(DeferredK.monadDefer())


Receives a function returning Kind<F, A>. The instance is responsible for creating and running the returned datatype lazily.

IO.monadDefer().defer { IO.just(1) }

This can be used to wrap synchronous APIs that already return the expected datatype, forcing them to be run lazily.


Suspends a function returning Unit. Useful in cases like Monad Comprehension where you’d want to defer the start of the comprehension until the datatype is run without needing to use suspend.

val SC = IO.monadDefer()

val result = SC.fx.monad {
  println("Print: now")
  val (result) = just(1)
  result + 1

//Print: now

val lazyResult = SC.fx.monad {
  println("Print: lazy")
  val (result) = eagerIO()
  result + 1

//Nothing here!

  .unsafeRunAsync { }

//Print: lazy


Takes as a parameter a function that returns Either<Throwable, A>. The left side of the Either represents an error in the execution. This function is assumed to never throw any internal exceptions.

  .laterOrRaise { throw RuntimeException() }
// ERROR!! The program crashes

laterOrRaise() exists for performance purposes when throwing can be avoided.


Arrow provides MonadDeferLaws in the form of test cases for internal verification of lawful instances and third party apps creating their own MonadDefer instances.

Data types

Module Data types
arrow.fx IO
arrow.fx.reactor FluxK, MonoK
arrow.fx.rx2 FlowableK, MaybeK, ObservableK, SingleK

Type Class Hierarchy

Do you like Arrow?

Arrow Org