Skip to main content

Outcomes and in-progress values

Arrow Core includes three different types to model success and failure: Option when no information is available on failure, Either when the success and failure cases are disjoint, and Ior when success and failure may happen at the same time. But sometimes life is a bit more complicated, and these type fall short. Fortunately, the wonderful Kotlin community has developed libraries to cover other scenarios, with full integration with Arrow's Raise approach.

Outcomes: absence is not failure

Quiver introduces the Outcome with three states: Present, Failure, and Absent. The last one is what sets it apart from Either.

val good = 3.present()
val bad = "problem".failure()
val whoKnows = Absent

In-progress values with Pedestal

Pedestal State introduces ProgressiveOutcome a type that combines the current state of a computation, and information about how the corresponding task is evolving.

Success does not mean stopped

The value Success(5, loading(0.4)) is perfectly valid, and describes a point in which:

  • The last successful value of a task is 5,
  • Retrieving the new value is at 40% completion.

To access the two components of a ProgressiveOutcome you often use Kotlin's destructuring. The first part is very similar to Outcome, in that it has Success, Failure, and Incomplete modes. The second element describes the current progress. In this case we may be Done, or the task may be Loading.

fun <E, A> printProgress(po: ProgressiveOutcome<E, A>) {
val (current, progress) = po
when {
current is Outcome.Success -> println("current value is ${current.value}!")
progress is Progress.Loading -> println("loading...")
progress is Progress.Done -> println("no value found :(")
}
}

Pedestal State includes helper functions onState that only execute when the value is in the corresponding state. These functions are especially useful to build a UI, where one often sees a component holding the current value and separate one describing the progress.

fun <E, A> printProgress(po: ProgressiveOutcome<E, A>) {
po.onSuccess {
println("current value is $it")
}
po.onLoading {
println("loading...")
}
}

Flows and States

One useful pattern in frontend applications is to combine one of these types with a Flow or a MutableState (if using Compose), to model the evolution of a piece of data through time. In fact, Pedestal State has a companion coroutines library including functions which help in using this pattern.