Option: fp-ts
Tags: typescript, functional
Published: 2020-07-03 11:53:23

<span>Photo by <a href="https://unsplash.com/@maiq?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Tom Morel</a> on <a href="https://unsplash.com/s/photos/aussie-outback?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a>, show him some love!</span>

# Introduction

I've fallen in love with `fp-ts` and the ecosystem. Once the main concepts are grasped and the code starts to flow, life is bliss. I will be very sad when my day job requires imperative style programming.

It solves many problems I've had coding, but I'll write about that another day.

One issue with the `fp-ts` ecosystem is that the docs are not designed for beginners; It's assumed you know the jargon and theory of functional programming.

The goal of this piece is to provide the information I was missing on my journey to aid those that may need it. If it's helpful please let me know somehow: like the post, otherwise I'm accessible via email or twitter.

We'll describe how to switch from using the built in `if`, `else` and `switch` keywords and using the `fp-ts` equivalents: `Option` and `Either` monads.

## Swap `if` for our saviour, `Option<A>`

Logic gates are a core of programming. More often than not, we're trying to assert if it IS something rather than isn't. If you're not a believer, work with `io-ts` for a week and you too can feel the joy of non-defensive programming.

Our current options for validation are:
- `throw new Error()` if the validation fails.
- Return `null` if the validation fails.

We will replace these with `Option<A>`, which represents null as `None` and the non-null value as `Some<A>`.

### Don't `throw new Error()`

Here our function of lovely ensures that our string `a` starts with the word `"lovely"`, otherwise it throws an `Error`.

```ts
function startsWithLovely(a: string): string {
  if(!a.startsWith("lovely")) {
    throw new Error()
  }
  
  return a
}

expect(startsWithLovely("lovely to meet you"))
  .toBe("lovely to meet you")

expect(startsWithLovely("nice to meet you"))
  .toThrowError()
```

The program will stop completely, with side effects you do or don't know about still running from earlier with null values (requests, responses).

If throw errors to catch them, we'll demonstrate some pure patterns in part 2/3.

Instead, let's try the next "best thing": `null`.

### Returning `null`

Here our function of lovely ensures that our string `a` starts with the word `"lovely"`, otherwise it return `null`.

```ts
function startsWithLovely(a: string): string | null {
  if(!a.startsWith("lovely")) {
    return null
  }
  
  return a
}

expect(startsWithLovely("lovely to meet you"))
  .toBe("Lovely to meet you")

expect(startsWithLovely("nice to meet you"))
  .toBe(null)
```

Our `startsWithLovely` function is now pure (nothing outside the function is changed).

The problem now is that we have to check that the value returned is not `null` whenever we want to use it. How annoying! 

### Our saviour, `Option<A>`

In functional programming, `null` is not used. The short story is that it was a mistake that gets worse in Javascript, because `typeof null === "object"`.

It's not obvious in these examples because we're working with primitives like `string` and `boolean`. Once we start mixing `null` and Javascript objects together, it'll feel like running with arthritic knees.

The `Option<A>` is one of the most common types (boxes) you'll use. 

```ts
import { option as O } from "fp-ts"

function oStartsWithLovely(a: string): O.Option<string> {
  if(!a.startsWith("lovely")) {
    return O.some(a)
  }
  
  return O.none
}
```

An improvement on this would be to use ternary operators, but instead we can use the constructor `O.fromPredicate`.

```ts
import { option as O } from "fp-ts"

function oStartsWithLovely(a: string): O.Option<string> {
  return O.fromPredicate(s => s.startsWith("lovely"))(a)
}
```

Or even one better:

```ts
import { pipe } from "fp-ts/lib/function"
import { option as O } from "fp-ts"

const startsWithLovely = (a: string): boolean =>
  a.startsWith("lovely")

const oStartsWithLovely = O.fromPredicate(startsWithLovely)
```

Note that `oStartsWithLovely` has the signature `(a: string) => O.Option<string>` across all three examples above.

## Composition with `Option`

Now we know when to use `Option`, let's look at how we can use it.


Our goal is to validate a string. It must meet all these conditions:
+ start with "lovely".
+ contain "meet".
+ end with "you".

The way we'd usually do this is with `if` and returning null, only returning the input value if it passes all the checks.

```ts
const example = (a: string): string | null => {
  const conditions = 
    a.startsWith("lovely") ||
    a.endsWith("you") ||
    a.includes("meet")

  if (conditions) {
    return a
  }

  return null
}

```

This might look normal to you. It did for me. Since we can control our own destiny, let's look at a few ways to use `Option`.

### Chain

Chain works analogously to `Array.map` then `Array.flatten`, but instead of an `Array<A>` we'll use `Option<A>`.

```ts
import { pipe } from "fp-ts/lib/function"
import { option as O } from "fp-ts"

const startsWithLovely = (a: string) =>
  a.startsWith("lovely")

const endsWithYou = (a: string) =>
  a.endsWith("you")

const containsMeet = (a: string) =>
  a.includes("meet")

const oExample = (a: string | null) =>
  pipe(
    a,
    O.fromNullable
    O.chain(O.fromPredicate(startsWithLovely)),
    O.chain(O.fromPredicate(endsWithYou)),
    O.chain(O.fromPredicate(containsMeet)),
  )
```

This is a lot better, but you see the pattern right? We're using `O.chain(O.fromPredicate(condition)))` over and over.

What if had to have 20 validation checks? copy-pasta should not the solution.

Let's refactor this with what might feel like pure magic.

```ts
import { pipe } from "fp-ts/lib/function"
import {
  option as O,
  array as A,
} from "fp-ts"

const startsWithLovely = (a: string) =>
  a.startsWith("lovely")

const endsWithYou = (a: string) =>
  a.endsWith("you")

const containsMeet = (a: string) =>
  a.includes("meet")

const conditions = [
  startsWithLovely,
  endsWithYou,
  containsMeet
]

const oExample = (a: string) =>
  pipe(
    conditions,
    // conjunction: all must be `Some`
    A.reduce(O.some(a), (oPrev, condition) =>
      pipe(
        oPrev,
        O.chain(O.fromPredicate(condition))
      )
    )
  )
```

This is putting all condition checkers in an array and iterating over each one, returning the results.

If you're coming from a non-functional background, this is a much better example than using and `A.foldMap(monoidAll)(condition => condition(a))(conditions) `.

# Parting words

This might seem like a jump in complexity, and it is to some extent. For simple examples it can be overkill, but what about more complex examples? The demand to create increasingly complex applications requires smarter, higher level abstractions. Functional programming fits abstraction neatly at a low level: code.

Programmers usually sit around the 85% percentile, so it's fair to say that we have the intellect to handle the abstraction. Some really smart people have conjured up the beauty of category theory, so let's follow in their footsteps.

If this helpful, I encourage you to utilize the functional paradigm and make the "boring" jobs fun again!


Part two will be on it's way soon.