From Null object Design pattern to Maybe Monad in TypeScript

Dimitris Papadimitriou
4 min readJan 24, 2020

The problem of null or undefined is one of the biggest problems in computation theory. The usual way object-oriented languages deal with this is by checking the types for not being undefined in order to use them. Any developer knows the number of bugs that have as source the problem of undefined.

The classical solution is using conditionals everywhere to check for null.

if (result) {
result.operation();
} else {
//do nothin
}

This reduces the cohesion of the codebase, because we must tightly couple to code relating to the cross-cutting concern of null checking.

The Null Object Design pattern

A more elegant solution to the problem of Null is the “Null Object” design pattern. The null object pattern is a special case of the strategy design pattern. The idea here is to replace conditional with strategy]

Instead of setting something as null we can set it as NullObject. Were the NullObject methods are empty, or they do not do anything when called. NullObject is in fact designed in a way that if it is fed to any method that waits for a RealObject, there should not be any unexpected side-effect, or any exceptions thrown.

A simple implementation of the NullObject design pattern with TS could be something like this :

This implementation is unobtrusive. It should not affect the rest of the code and should remove the need to check for null. In essence we moved the effect for null checking to our sum type AbstractObject = NullObject + RealObject.

Thake as an example the more realistic problem that we are going to build upon. Let’s say we want to get a Client from a repository by Id

Run This: TS Fiddle

We want to display the name without having to check for nulls :

console.log(new MockClientRepository().GetById(1).Display(v => v.Name));

if there was no client we would still use the same syntax, without exceptions

console.log(new MockClientRepository().GetById(5).Display(v => v.Name));

The major problem with this pattern is that for each object, we need to create a NullObject with the operations that we are going to use inside the rest of the code. This will force us to duplicate all the domain objects in our model. It is a big trade-off. There is no easy way to abstract the mechanics of this implementation in an object-oriented world, mainly because we cannot have specific information about the operations that we want to isolate and create a NullObject that provides those operations.

The Functional equivalent — Maybe as Functor

Now the Maybe functor idea takes this line of reasoning one step further, by abstracting the null Object mechanism inside a functor. Thus, instead of applying the objects on functions, we reverse the flow by applying the functions onto objects. Now, we can isolate the effect inside a single point; the “map.”

After all this discussion, hopefully, the implementation of maybe functor would be apparent

Run This: TS Fiddle

Now let us revisit the problem of fetching a client asynchronously with a certain Id from a repository.

Here we want to replace the result of the array filtering

.find(c => c.Id === id)

with something that will return a Maybe<Client>. If there is no client for a specific id, we should return None(). If there is a client, we will return Some(client). We will define an array extension that will do just that :

Where I used the recursive extension of the list that we have seen earlier in order to provide a matchWith functionality. Now we can write:

Run This: TS Fiddle

Maybe as Monad

In order to make this maybe implementation into a Monad we have to provide a bind method that combines two Maybe monads into one. There are 4 different ways to combine the 2 possible states of maybe (some,none)one can see that a valid implementation would be the following :

Run This: TS Fiddle

there are four possible combinations of 2 maybe’s

some(x).bind(none)  A
none( ).bind(some) B
some(x).bind(some) C
none( ).bind(none) D

the only combination that leads to a new some(_) is when we combine the the two some(_) paths .

Librariespurify: https://gigobyte.github.io/purify/adts/Maybefp-ts: https://gcanti.github.io/fp-ts/modules/Option.ts.html

Excerpt from the book :

https://leanpub.com/functional-programming-in-Ts-with-categories

--

--

Dimitris Papadimitriou
Dimitris Papadimitriou

No responses yet