Skip to content

1. Maybe

Carlos De Dios edited this page Sep 24, 2015 · 2 revisions

What if I get Nothing?

In javascript the falsey values really don't make too much sense, you don't have a valid 'absence of value', so, for any type you can get undefined as absence, which is conceptually wrong, given that undefined means lack of existence, but it does exist, it just doesn't have a value. And this is where the 'wrapper' Maybe comes into play.

Imagine the following scenario, you have a factory which searches in a collection by a provided Id, however, if said Id is not found you would get undefined instead, said factory uses an Object created by the user so, it has custom properties on its prototype the you wish to call after fetching, you have workarounds to verify the nullability of the result, but, why not simply doing what you have to do when you have to do it, (say, when it's not undefined);

For the sake of brevity let's imagine the factory only holds 20 items.

let searchById = (id) => Factory.retrieve({ id, single: true });

let updateFactory = (id, { updatedItem }) => {
  let item = searchById(id).applyUpdate(updatedItem);
  Factory.update({ id, item });
  return Public.unit;
}

/**
 * Typical Vanilla Approach
 */

let item = searchById(21);

let updatedItem = item.barrelRoll(); // This goes wrong.
updateFactory(21, { updatedItem }); // This goes even worst.

Of course, it can be fixed with a simple if

let item = searchById(21);

if (item) {
  let updatedItem = item.barrelRoll();
  updateFactory(21, { updatedItem });
} else {
  // ...
}

But this sort of uglifies the code, you can perceive the intention, but it seems like the 'else' path leads astray, is doing nothing, it's returning nothing, when you object has an invalid state, what do you do?

/**
 * Maybe Approach
 */

Maybe.unit(searchById(21)).match({
  just: item => Maybe.unit(item.barrelRoll()),
  nothing: Maybe.nothing
}).match({
  just: updatedItem => updateFactory(21, { updatedItem }),
  nothing: Maybe.nothing
});

The Maybe path tries to lead you into doing everything on it's own space, you first search, if there's nothing, then, tell me there's nothing, if there's something do a BarelRoll, if your BarelRoll wasn't successfull give me nothing, otherwise let's update you.

Select

The select method assumes you're only wanting to transform in case the provided Maybe is a Just otherwise it ignores the provided transformation and return a Nothing, for example.

let maybeTen = Maybe.unit(10).select(x => x * x); // yields Just 100
let maybeNothing = Maybe.unit(undefined).select(x => x * x); // yields Nothing

The same example used previously with the factory could be written using select to simplify the process, given that we do not use the Nothing on any point.

Maybe.unit(searchById(21))
     .select(item => item.barrelRoll())
     .select(updatedItem => updateFactory(21, { updatedItem });

Any point in the process that yields a Nothing will carry a Nothing to the end and have it as the final result.

SelectMany

Now, what if I have several Maybe's I want to unwrap and use their values, what could I do to keep the safety a Maybe provides?

Simple selectMany is a method that from a Maybe takes a tranformation function that returns a Maybe it sounds confusing, but it's easier to see when mixed with select.

let maybeTen = Maybe.unit(10);
let maybeTwenty = Maybe.unit(20);

let maybeThirty = maybeTen.selectMany(ten => maybeTwenty.select(twenty => ten + twenty)); // yields Just 30

Woah, slow down chief, what just happened? We can ilustrate this a little bit differently by not using arrow functions, let's see.

let maybeThirty = maybeTen.selectMany(function(ten) { // selectMany takes a parameter, which is provided as the unwrapped value
  return maybeTwenty.select(function(twenty) { // same case with select
     return ten + twenty; // here we have both Maybe's unwraped, however, if any of them is a Nothing this block of code will never be reached.
  });
});

Clone this wiki locally