Use JavaScript functor today

Replace if conditions with functors and applicatives.

How often do you guard against an undefined object before executing code?

1
2
3
4
5
6
7
function getItem() {
return ...;
}
var item = getItem();
if (item) {
console.log(item, 'length', item.length);
}

You have to guard because reading the property item.length crashes if the item is null or undefined. Let us replace the if condition with a functor - a wrapped value and function call. First, let us refactor the above code to cleanly separate the print function

refactored code
1
2
3
4
5
6
7
var item = getItem();
function print(x) {
console.log(x, 'length', x.length);
}
if (item) {
print(item);
}

Second, instead of using the value directly, let us wrap it in an object that provides .then method. We will use this .then method to run the print function.

functor
1
2
3
4
5
6
7
8
9
10
11
function Maybe(x) {
return {
then: function (fn) {
if (x !== undefined && x !== null) {
return fn(x);
}
}
};
}
var item = getItem();
Maybe(item).then(print);

The print is now safe

1
2
3
4
Maybe('foo').then(print);
// foo length 3
Maybe().then(print);
// does nothing

Notice that the condition if (x !== undefined && x !== null) is hard-coded by the Maybe function. We can pass this condition into the functor factory.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function isDefined(x) {
return x !== undefined && x !== null;
}
function Conditional(condition, x) {
return {
then: function (fn) {
if (condition(x)) {
return fn(x);
}
}
};
}
Conditional(isDefined, 'foo').then(print);
// foo length 3
Conditional(isDefined).then(print);
// does nothing

I placed the condition function first because I believe it will change less often than the data. You don't have to write the Conditional functor yourself, there is an entire library of predicates we have written to simplify condition checking: check-more-types. One of the methods is check.then. In our case we can use it to safely print the property

1
2
3
4
5
var check = require('check-more-types');
check.then(isDefined, print)('foo');
// foo length 3
check.then(isDefined, print)();
// does nothing

In above example the expression check.then(isDefined, print) is an applicative - a wrapped function to be applied to a value. The good aspect of this design is the ease with which you can create safe versions of functions like print.

1
2
3
4
5
var safePrint = check.then.bind(null, isDefined, print);
safePrint('foo');
// foo length 3
safePrint();
// does nothing

Conclusion

The functor or applicative code is clearner and safer than always using the if expression. When using a functor we wrap the individual value and use the original function. When using an applicative we wrap the function, making it safe to use with any value.

More examples of functors in Combine promises with Maybe functors.