Skip to content

Choosing a combinator

The method surface is small, but "which one do I reach for?" comes up constantly. This page is the cheat sheet. Every combinator runs its callback only on its own channel and turns a thrown callback into a Defect.

By intent

I want to…usechannel
transform the success valuemapOk
run a dependent, Result-returning stepflatMapOk
run a side effect, keep the valuetapOk
run a failable side effect, keep the valueflatTapOk
sequence dependent steps into a named scopeDo/bind/letOk
replace the value with a constantasOk
transform the errormapErrErr
try a fallback that returns a ResultorElseErr
turn an error into a success valuerecoverErr
run a side effect on the errortapErrErr
run a failable side effect on the errorflatTapErrErr
recover from a defect (rare)recoverDefectDefect
observe a defect, e.g. log ittapDefectDefect
handle all three channels at the edgematchall
combine many Resultsall / allAsync

The pairs that are easy to confuse

map vs flatMap — does your callback return a plain value or a Result? A (value) => U is map; a (value) => Result<U, E2> is flatMap (otherwise you get a nested Result<Result<…>>).

tap vs flatTap — both keep the original value. tap takes a void callback that can't fail; flatTap takes a Result-returning one and threads its error (a validation or write whose outcome matters but whose value you don't need).

flatMap vs flatTap — both take a Result-returning callback. flatMapreplaces the value with the callback's; flatTap discards the callback's value and keeps the original.

tapErr vs flatTapErr — the error-channel mirror of tap vs flatTap. Both run only on Err and keep the original error on success. tapErr takes a void callback that can't fail; flatTapErr takes a Result-returning one and threads its error (a failable effect during error handling — e.g. writing the error to an audit log that may itself fail).

orElse vs recover — both run on Err. recover produces a plain success value (emptying the error channel to never); orElse produces another Result (which may still be an Err).

recover vs recoverDefectrecover handles a modeled Err; recoverDefect is the only combinator that can touch a Defect. Neither is the other's fallback: a defect flows past recover untouched.

When to leave the pipeline

Reach for an eliminator once you're done chaining:

  • match — the default at the edge; fold all three channels into one value.
  • unwrap / unwrapErr — extract, throwing on the wrong variant (and panicking on a defect).
  • unwrapOr / unwrapOrElse / getOrNull / getOrUndefined — recover an Err to a fallback, but re-throw a defect (it's a bug, not an absent value).

→ Continue to Recipes.

Released under the MIT License.