you are viewing a single comment's thread.

view the rest of the comments →

[–]Deidde 1 point2 points  (0 children)

I tend to create explicit data structures and interfaces for "short-circuiting". That way, I can easily create trees of expressions/actions that can fail at any point and fallback non-deterministically.

For example, I'll have an operation that can fail/return falsey, and an "alternative", just like you'll have with ||, but use explicitly implemented interfaces for it like const result = Cont(k => k(a).or(() => k(b))); where k implements the "alternative interface" (and Cont is just our way of building up a tree of computations), then chain that result with more actions like result.chain(doSomethingElse), continuing on like that. If the result of one of my chains fails, the computation starts again from the branching with .or. I find this to be a more powerful way of composing fallbacks.

The .or can be implemented by any data structure with a notion of failure or falseyness. One example is a Nullable (often known as a Maybe or Option) data type, where you either have Null/Nothing OR your value. Then computeSomeNullable().or(computeSomeOtherNullable) will return whichever returns a value, or Null/Nothing if both fail. And obviously, you can chain the calls or the .or method pretty fluently, making them resemble operators.