Daniel Lowman

Maybe?

August 30, 2018

This article is centred around my use of TypeScript however it can also be applied to JavaScript or any other language which treats functions as first class citizens. I also do not deem myself as an expert in functional programming so if you notice any issues please let me know.

Handling bad cases within TypeScript can be quite verbose, if a type is optional and can either be of value undefined or null, this has lead to me both reading and writing verbose checks to handle these cases. For example, picture the below type:

interface User {
    firstname?: string,
    lastname?: string,
    email?: string,
    address: {
        postcode?: string
    }
}

All of its properties are optional, potentially the type too if you specify it. If you created a new object literal of type User and wanted to get the value of firstname you would have to check if the firstname field is not of type undefined or null. These checks can become super verbose with large complex types along with being tedious to write and opens up the surface area of your code to requiring more test coverage.

If you think about it, all of these operations you perform on this value could be extracted into a generic form. Enter the Maybe monad.

The Maybe monad encapsulates computations which could potentially go wrong and allows you to use a monadic form to handle this. Creating an object literal of the above type:

const user: User = {
    firstname: 'First',
    lastname: 'Last',
    email: undefined,
    address: {
        postcode: undefined
    }
}

I can create a Maybe of this:

const userMaybe = Maybe.of(user);

Encapsulating this will yield a Maybe of type user or null, I can then use the isNothing function to check if it is indeed ‘nothing’ (undefined or null).

userMaybe.isNothing() ==> false

I can also project it to another Maybe:

userMaybe.map(x => 'Something else')

Using the .join() function I can get the value within the Maybe:

userMaybe.join() ==> {
    firstname: 'First',
    lastname: 'Last',
    email: undefined,
    address: {
        postcode: undefined
    }
}

Notice how it will yield the original object as the map above yields a new Maybe and does not mutate itself. I can also make use of the orElse function to return a new Maybe containing the value specified in the case of the maybe resulting to nothing.

userMaybe.map(x => x.email)
         .orElse('No email')
         .join()
==> 'No email'

Using a generic type such as a Maybe allows you to encapsulate your other types within them and make use of the operations you would usually write in a repetitive imperative way in a safeguarded monadic collection of expressions along with reducing the scope for human error and reducing the amount of conditionals you would typically have to write.


Daniel Lowman

I'm a software engineer with a various range of experience with different technologies. I really enjoy functional programming and teaching others.