Left Fold

Fallbacks with Alt

If you have several values which might be null, and you want the first ‘truthy’ value, one way is to use a sequence of ORs:

const c = a || b || 0

The trouble is, while this style is concise, it only works if the only values you care about are ‘truthy’. false, null, undefined, 0, NaN and "" all evaluate as false. This is a problem for our example, because 0 is a valid value for c, but if a is 0, we still fallback to b:

const a = 0
const b = 1
const c = a || b || 0
//=> c === 1

So a more correct version might be:

const c = Number.isInteger(a)? a : 
                (Number.isInteger(b)? b : 0)

A downside to this is that code working with a and b has to know how to test whether a and b are values we can use in this context. This test logic may have to be duplicated, leaking into other parts of the code.

Maybe lets us push this logic upstream into the functions accessing the values in the first place - returning Just(Integer) or Nothing instead of Integer or null (or undefined or …). We already know how to fallback from a Maybe to a default value with .getOrElse(defaultValue). But how can we fall back from one Maybe to another?

const a = Nothing()
const b = Just(1)
const c = a.getOrElse(b.getOrElse(0))

This is one way, but the nesting, like all nesting, is a little painful, and will get more painful as we increase the number of Maybes we need to fallback through.

A nicer way is to use the alt algebra from Fantasy Land. We can define it for Maybe very simply:

const Just = x => ({
   alt: A => Just(x)
// also define map, chain, getOrElse ... 
})
const Nothing = () => ({
   alt: A => A
// also define map, chain, getOrElse ... 
})

or, if Maybe is defined in a prototype style:

Just.prototype.alt = function(A){ return this }
Nothing.prototype.alt = function(A){ return A }

In other words, Just(something).alt(A) always returns Just(something) and Nothing().alt(A) always returns A.

This lets us write

const c = a.alt(b).getOrElse(0)

and fallback through many Maybes without nesting:

const z = y.alt(x).alt(w).alt(v).getOrElse(42)

(At the time of writing, Sanctuary is the only library to implement Maybe.alt - but it may be about to move to fantasyland/fantasy-maybes)

Alt Tasks

You can also imagine implementing it for Task:

const Task = fork => ({
    alt: A => Task(function(reject, resolve){
        fork(
            err => { A.fork(reject, resolve) },
            success => resolve(success)
        )
    }),
    fork
    //define map, chain, ap...
})
Task.of = x => Task((_, resolve) => resolve(x))

Used like:

writeToA
.alt(writeToB)
.alt(writeToC)
.fork(
    err => console.error("failed to write to A, B & C", err),
    success => console.log("Wrote successfully to one of A, B or C", success)
)

Alt Task Maybe

Slightly trickier is alternating between a Task Maybe composition. eg, you want to perform a lookup on one server, and if you don’t find a result, look on a second server.

const doubleAlt = A => B => A.chain(a => {
   const empty = a.constructor.empty()
   return a.alt(empty).equals(empty)? B : A.constructor.of(a) 
}).alt(B)

const A = Task.of(Nothing())
const B = Task.of(Just(42))

doubleAlt(A, B)
.map(m=>m.getOrElse(0))
.fork(
      console.error
    , console.info
) // => 42

How doubleAlt works is, we use chain to look inside the first Task, and if the inner Maybe is equal to an empty Maybe (ie, a Nothing), we return the second Task. Otherwise, we return the first Task. In addition, we use .alt on the outer Task to catch the first Task rejecting.

Posted December 13, 2017
READ THIS NEXT:

How to handle an uncertain Future, with Task.empty

In my previous post introducing Maybe, I ended with an example using Promises, mentioning that Task can be a useful alternative to javascript Promises. This was the example: getMaybeUserName() .map...


author Keith AlexanderWritten by Keith Alexander. Interests include functional programming, web tech, event sourcing and linked data.