Either: fp-ts
Tags: typescript, functional
Published: 2020-12-05 04:09:22

<span>Photo by <a href="https://unsplash.com/@tobyelliott?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Toby Elliott</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span>

# Introduction

I received an email from an Aussie admirer of my [last post](https://dev.to/waynevanson/fp-ts-meets-if-else-and-switch-part-1-3-2lpf) asking for part two of the series.
Deja Vu, here we are again with another fp-ts data structure for handling conditional logic.

We've [already explored](https://dev.to/waynevanson/fp-ts-meets-if-else-and-switch-part-1-3-2lpf) the data structure `Option<A>` as a functional replacement to handle `if` statements.

Today we'll gear our minds towards handling what happens when `if` needs an `else`.

# Signature

```ts
type Either<E, A> = Left<E> | Right<A>;

interface Right<A> {
  _tag: "right";
  value: A;
}

interface Left<E> {
  _tag: "Left";
  value: E;
}
```

`Left` and `Right` can be distinguished with the `_tag` property on the object, which is available at runtime. It's leveraged by the functions within the module to map over.

# replace `if`/`else` with `Either`

Our goal will be to create a `Person` struct, where the constraint is that a `name` must be letters only, no spaces, no numbers.

If it does not match this, we need options to handle it (functionally).

```ts
interface Person {
  name: string;
}
```

## Using the language

```ts
function validateName(name: string): string {
  // regex for letters only
  if (/[a-zA-z]/.test(name)) {
    return name;
  } else {
    return "not a valid name!";
  }
}
```

We can use the ternary operator to make this a lot smaller, but the logic is the same.

```ts
const validateName = (name: string): string =>
  /[a-zA-z]/.test(name) ? name : "not a valid name!";
```

Did you notice the return value is `string`, regardless of whether it is valid or not?

If we wanted to differentiate the return value with the same type (string, number, etc), we must put it in a box/data structure.

## Using fp-ts

Let's use the `Either` data structure and see how it looks.

### Constructors

```ts
import { either as E } from "fp-ts";

// (name: string) => Either<string, string>
const validateName = E.fromPredicate(
  (string: string) => /[a-zA-z]/.test(string),
  (name) => `"${name}" is not a valid name!`
);
```

`fromPredicate` is a function derived from the `MonadThrow` typeclass.

It is a `constructor`, meaning it can create the data structure. In this case it creates an `Either` using a predicate function.

### Combinators

Now because we're using a data structure with the familiar `fp-ts` API, we have access to all other combinators applicable to this structure.

These can be found here:

https://github.com/gcanti/fp-ts/blob/9ff0cb6a7264c03254ff60232fb44dba3841a340/src/Either.ts#L1262-L1290

We'll use the functions `map` and `mapLeft`, derived from the `MonadThrow` typeclass.

```ts
// example of inline function composition
import { either as E } from "fp-ts";
import { flow } from "fp-ts/function";

interface Person {
  name: string;
}

// (name: string) => Either<string, Person>
const makeUser = flow(
  E.fromPredicate(
    (name: string) => /[a-zA-z]/.test(name), 
    (name) => `"${name}" is not a valid name!`
  ),
  // applies the function over `Right`, if it is `Right`
  E.map((name): Person => ({ name })),
  // applies the function over `Left`, if it is `Left`
  E.mapLeft((message) => new Error(message))
);
```

Nothing stops us from composing our functions inline as demonstrated.

Since we're using functions, let's split out some inline functions. We do this when we need to use them elsewhere in our hypothetical code base or if it's easier for you to read.

```ts
// example of separated functional composition
import { either as E } from "fp-ts";
import { flow } from "fp-ts/function";

interface Person {
  name: string;
}

const regexLetters = /[a-zA-z]/;

// (name: string) => Either<string, string>
const validateName = E.fromPredicate(
  (name: string) => regexLetters.test(string),
  (name) => `"${name}" is not a valid name!`
);

const makeError = (message: string) => new Error(message);

// (name: string) => Either<Error, Person>
const makeUser = flow(
  validateName,
  E.map((name): Person => ({ name })),
  E.mapLeft(error)
);
```

Beautiful. Now we can use these functions where ever we may. The most useful I think is `regexLetters` and `makeError`

### Destructors

Well there are a few destructors available, so we'll use the `fold` and `getOrElse` functions.

`fold` takes two functions, where the first is a case for `Left` and the second is a case for `Right`.

```ts
// using fold
import { either as E } from "fp-ts";
import { flow } from "fp-ts/function";

interface Person {
  name: string;
}

const regexLetters = /[a-zA-z]/;

const validateName = E.fromPredicate(
  (name: string) => regexLetters.test(regexLetters),
  (name) => `"${name}" is not a valid name!`
);

const makeError = (message: string) => new Error(message);

const makeUser = flow(
  validateName,
  E.map((name): Person => ({ name })),
  E.mapLeft(makeError)
);

// (name: string) => string
const main = flow(
  makeUser,
  E.fold(
    (error) => error.message,
    ({ name }) => `Hi, my name is "${name}"`
  )
);

expect(main("Wayne"))
  .toMatchObject(E.right(`Hi, my name is "Wayne"`));

expect(main("168"))
  .toMatchObject(E.left(`"168" is not a valid name!`));
```

An alternative option is to use `getOrElse`, which we can use if we don't need to change the output of the `Right` value in `Either`

```ts
// using getOrElse
import { either as E } from "fp-ts";
import { flow } from "fp-ts/function";

interface Person {
  name: string;
}

const regexLetters = /[a-zA-z]/;

// (name: string) => Either<string, string>
const validateName = E.fromPredicate(
  (name: string) => regexLetters.test(name),
  (name) => `"${name}" is not a valid name!`
);

const makeError = (message: string) => new Error(message);

const makeUser = flow(
  validateName,
  E.map((name): Person => ({ name })),
  E.mapLeft(makenerror)
);

// (name: string) => string | Person
const main = flow(
  makeUser,
  // `W` loosens the constraining result type,
  // otherwise it would force us to make it `Person` type.
  E.getOrElseW((error) => error.message)
);


expect(main("Wayne"))
  .toStrictEqual({ name: "Wayne" });

expect(main("168"))
  .toBe(`"168" is not a valid name!`);
```

There a few more, but these are the most common and I rarely use the rest.

# Recommended Reading

The next step is tying all your error handling by implementing [gcanti's guide to `Either` and `Validation`](https://dev.to/gcanti/getting-started-with-fp-ts-either-vs-validation-5eja).

# Notes

I find using fp-ts scales a project way better, where enforcing the constraints of "pure" functional programming really makes a difference when practicing domain driven development.

Keep in mind it's not worth folding/destructing your data structure until you have to. Usually there is a `main` function that is the entry point into an application and this where most of my folding happens.

It's up to your taste if you like the functions separated or inline.
But when you need to use the same code more than twice in a code base, the separated functional approach is what you may lean towards.