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.
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...")
}
}
Flow
s and State
s
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.