Functional Type Safety in Javascript with Maybe
Tags: javascript, functional, maybe, crocks
Published: 2019-07-13 17:38:26

> In programming languages (more so functional programming languages) and type theory, an option type or maybe type is a polymorphic type that represents encapsulation of an optional value; e.g., it is used as the return type of functions which may or may not return a meaningful value when they are applied. It consists of a constructor which either is empty (often named None or Nothing), or which encapsulates the original data type A (often written Just A or Some A).
> -- <cite>[Wikipedia](https://en.wikipedia.org/wiki/Option_type)</cite>

Let's talk about what this means, why it's useful, and how to utilize the concept in Javascript.

Javascript is an untyped language, which makes it very flexible, and in some cases, very powerful. But with that power comes great responsibility. Take for instance, a function designed to operate on a string:

```javascript
const capitalize = a => a.charAt(0).toUpperCase() + a.slice(1)

capitalize('javascript') //=> "Javascript"
```

Now substitute the string for any other datatype:

```javascript
capitalize(5) //=> a.charAt is not a function
capitalize(true) //=> a.charAt is not a function
capitalize(['javascript']) //=> a.charAt is not a function
capitalize(null) //=> Cannot read property 'charAt' of null
capitalize(undefined) //=> Cannot read property 'charAt' of undefined
```

Anyone who's done a fair amount of Javascript will recognize that mismatched datatypes and null/undefined are a common source of run-time errors. There are, of course, various ways to write safer functions, often referred to as defensive programming:

```javascript
const capitalize = a => (typeof a === 'string') 
  ? a.charAt(0).toUpperCase() + a.slice(1) : ''
```

While this is a much safer version, it can add a lot of code cruft, especially when you need these types of checks scattered all throughout your code base. In addition, it forces you to think (and therefore write) in a more imperative way, rather than a more expressive way that functional programming promotes.

The way we deal with null/undefined or type mismatches depends on whether they should be expected and whether the data can be controlled. For example, if we want to capitalize each part of a person's name, and the middle name isn't a requirement in our data, we can expect it to be unavailable (or null) when given to a function. In that case, we'd, ideally, prefer to just skip the function call and let the rest of code continue its execution. This is one of the benefits we get from the `Maybe` data type. 

A `Maybe` is a [Sum Type](https://en.wikipedia.org/wiki/Tagged_union) that can represent one of two other types; a `Just` or `Nothing` (or a `Some`/`None`, depending on the language). You can think of it as a polymorphic relationship where `Just` represents a correct or valid value, and `Nothing` represents an incorrect, invalid, or lack of value (such a null). 

Both `Just` and `Nothing` act as a container, or wrapper, for raw data. The significance of this is that functions that know how to work with a `Maybe` can also work with `Just` or `Nothing`, even if the raw data is invalid. Each of these wrappers have the same API, allowing them to be interchangeable. 

This isn't so different from the way Javascript primitives work. When you execute code such as `'javascript'.toUpperCase()`, it's not the string, itself, that has the `toUpperCase()` function attached to it. After all, string is a primitive, meaning it has no functions or properties. Instead, it's the String() constructor that has the `toUpperCase()` function, and Javascript will auto-wrap the primitive when calling contructor fuctions/properties on it.

Let's look at some actual code. For the examples in this article we'll be using the [Crocks](https://crocks.dev) library.

There are multiple ways to construct a `Maybe` data type, such as using the `Maybe` constructor itself:

```javascript
const Maybe = require('crocks/Maybe')

Maybe('javascript') //=> Just "javascript"
Maybe.of('functional') //=> Just "functional"
Maybe.of(null) //=> Just null
```

The `Maybe` constructor will always produce a `Just`. It's recommended to use the `Just` and `Nothing` constructors directly, if only for readability:

```javascript
Maybe.Just() //=> Just undefined
Maybe.Just('javascript') //=> Just "javascript"
Maybe.Nothing() //=> Nothing
Maybe.Nothing('javascript') //=> Nothing
```

You can also destructure `Just` and `Nothing` to tighten up your code:

```javascript
const Maybe = require('crocks/Maybe')
const {Just, Nothing} = Maybe

Just() //=> Just undefined
Nothing() //=> Nothing
```

But most of your `Maybe` types will be produced from helper functions. The focus of this article will be on the [safe](https://crocks.dev/docs/crocks/Maybe.html#safe) helper function.

`safe` takes a predicate function, which returns a boolean, and a value to  be applied to the predicate. If the predicate returns true, we get a `Just`, otherwise, a `Nothing`:

```javascript
const Maybe = require('crocks/Maybe')
const safe = require('crocks/Maybe/safe')

const isString = a => (typeof a === 'string') 

safe(isString, 'javascript') //=> Just "javascript"
safe(isString, 5) //=> Nothing
safe(isString, null) //=> Nothing
```

Safe is curried, allowing us to preconfigure it with a predicate and pass in the data later. For brevity, we'll also switch to Crock's builtin `isString` function:

```javascript
const Maybe = require('crocks/Maybe')
const safe = require('crocks/Maybe/safe')
const isString = require('crocks/predicates/isString')

const safeString = safe(isString)

safeString('javascript') //=> Just "javascript"
safeString(5) //=> Nothing
```

A `Maybe` (and therefore `Just` and `Nothing`) implements a wide range of algebraic structures, one of which is the [Functor](https://hackernoon.com/functors-in-javascript-20a647b8f39f) allowing us to [map](https://crocks.dev/docs/crocks/Maybe.html#map) a `Maybe`.

One of the rules of a Functor is that when we map a value to another, we get back the same type and structure.  If we map an array, we'll get back an array of the same size (with differing values). If we map a `Maybe` we'll get back a `Maybe`. We're only affecting the raw data inside. Let's go back to our original caplitalize function and map it to our `Maybe`s:

```javascript
const safeString = safe(isString)
const capitalize = a => a.charAt(0).toUpperCase() + a.slice(1)

safeString('javascript').map(capitalize) //=> Just "Javascript"
safeString(5).map(capitalize) //=> Nothing
safeString(null).map(capitalize) //=> Nothing
```

When we map a valid (`Just`) value, the mapping will unwrap the raw data from our `Maybe`, pass it into the mapper (`capitalize`), and re-wrap the result. When we try to map an invalid (`Nothing`) value, the mapper will be ignored and just return a new `Nothing`.

The thing to point out here is that our `capitalize` function is just a regular Javascript function without any type checks or null checks. In fact, we don't have any type/null checks anywhere in our code. That's all abstracted away in the `Maybe` type. Passing a `safeString` to capitalize is guaranteed to be error free.

Another thing I'll point out is that an invalid value doesn't have to be only values that produce an error. For example, an empty string could be safely passed to capitalize, but there'd be no point. If we rewrote our `safeString` function to exclude empty strings from being valid (and rename it to `validString`) we could avoid the performance cost of executing the capitalize function. This would become more value when implementing expensive operations such as making a service call.

Finally, there will come a time when you're ready to unwrap the raw data and discard the `Maybe` container. This would usually be at the end of the flow, such as rendering the value on screen, or passing it to a service method. This can be done with `Maybe`'s [option](https://crocks.dev/docs/crocks/Maybe.html#option) function:

```javascript
safeString('javascript').map(capitalize).option('') //=> 'Javascript'
safeString(5).map(capitalize).option('') //=> ''
safeString(null).map(capitalize).option(null) //=> null
```

`option` takes a single parameter, a default value, to use when unwrapping a `Nothing`. When unwrapping a `Just`, the default is ignored and the raw data is returned. I would caution against unwrapping your data too early. There isn't anything that can be done to raw data that can't also be done to the same data when wrapped. I've shown an example of transforming wrapped data with map, but there's many more functional applications for wrapped data.

This was very much an introduction to type safety with `Maybe`. There are many more useful applications with `Maybe` as well as other structures to help write error free code in an expressive way. I'll be writing a future post on `Either`, a structure that allows you to work with errors (instead of just avoiding the execution of unsafe code) and eliminating the use of `try`/`catch`.