Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/inato/fp-ts-cheatsheet

FP-TS Cheat Sheet
https://github.com/inato/fp-ts-cheatsheet

fp-ts functional-programming immutable monads typescript

Last synced: 12 days ago
JSON representation

FP-TS Cheat Sheet

Awesome Lists containing this project

README

        

# :memo: `fp-ts` cheatsheet

More detailed explanations can be found in the [Details](Details.md) document.

1. [Imports](#imports)
2. [Pipe and Flow](#pipe-flow)
3. [Option](#option)
4. [Either](#either)
5. [TaskEither](#taskeither)
6. [Array and ReadonlyArray](#array)
7. [Do-notation](#do-notation)

## Imports

```ts
import { either, option } from 'fp-ts'; // import modules from root
import { flow, pipe } from 'fp-ts/functions'; // import functions from functions
import { Either } from 'fp-ts/Either'; // import types from their module

// rename common long module names
import { readerTaskEither as rte } from 'fp-ts';
```

## Pipe and Flow

```ts
const value = 'value';

// Imperative style
const value1 = addSthg(value);
const value2 = doSthgElse(value1);
const finalValue = doFinalSthg(value2);
// Or maybe inline
const finalValue = doFinalSthg(doSthgElse(addSthg(value)));

// With pipe
const finalValue = pipe(value, addSthg, doSthgElse, doFinalSthg);

// With flow
const transformations = flow(addSthg, doSthgElse, doFinalSthg);
const finalValue = transformations(value);
```

## Option

### Create an `Option`

```ts
// Build an Option
option.none;
option.some('value');

// Build from a value
option.fromNullable(null); // => option.none
option.fromNullable('value'); // => option.some('value')

// Build from a predicate
const isEven = number => number % 2 === 0;

option.fromPredicate(isEven)(3); // => option.none
option.fromPredicate(isEven)(4); // => option.some(4)

// Convert from another type (eg. Either)
const leftEither = either.left('whatever');
const rightEither = either.right('value');

option.fromEither(leftEither); // => option.none
option.fromEither(rightEither); // => option.some('value')
```

#### Extract inner value

```ts
const noneValue = option.none;
const someValue = option.of(42);

// Convert Option to T | undefined
option.toUndefined(noneValue); // => undefined
option.toUndefined(someValue); // => 42

// Convert Option to T | null
option.toNullable(noneValue); // => null
option.toNullable(someValue); // => 42

// Convert Option with a default value
option.getOrElse(() => 0)(noneValue); // => 0
option.getOrElse(() => 0)(someValue); // => 42

// Convert Option to T | U with a default value of type U
option.getOrElseW(() => 'default')(noneValue); // => 'default': number | string

// Apply a different function on None/Some(...)
const doubleOrZero = option.match(
() => 0, // none case
(n: number) => 2 * n // some case
);

doubleOrZero(noneValue); // => 0
doubleOrZero(someValue); // => 84

// Pro-tip: option.match is short for the following:
const doubleOfZeroBis = flow(
option.map((n: number) => 2 * n), // some case
option.getOrElse(() => 0) // none case
);
```

## Either

### Create an `Either`

```ts
// Build an Either
const leftValue = either.left('value');
const rightValue = either.right('value');

// Build from a value
either.fromNullable('value was nullish')(null); // => either.left('value was nullish')
either.fromNullable('value was nullish')('value'); // => either.right('value')

// Build from a predicate
const isEven = (num: number) => num % 2 === 0;

const eitherBuilder = either.fromPredicate(
isEven,
number => `${number} is an odd number`
);

eitherBuilder(3); // => either.left('3 is an odd number')
eitherBuilder(4); // => either.right(4)

// Convert from another type (eg. Option)
const noneValue = option.none;
const someValue = option.some('value');

either.fromOption(() => 'value was nullish')(noneValue); // => either.left('value was nullish')
either.fromOption(() => 'value was nullish')(someValue); // => either.right('value')
```

### Extract inner value

```ts
const leftValue = either.left("Division by Zero!");
const rightValue = either.right(10);

// Convert Either to A with a default value
either.getOrElse(() => 0)(leftValue); // => 0
either.getOrElse(() => 0)(rightValue); // => 10

// Apply a different function on Left(...)/Right(...)
const doubleOrZero = either.match(
(error: string) => {
console.log(`The error was ${error}`);
return 0;
},
(n: number) => 2 * n,
);

doubleOrZero(leftValue); // => 0 - also logs "The error was Division by Zero!"
doubleOrZero(rightValue); // => 20

// Pro-tip: either.match is short for the following:
const doubleOrZeroBis = flow(
either.map((n: number) => 2 * n),
either.getOrElse((error: string) => {
console.log(`The error was ${error}`);
return 0;
});
);
```

## TaskEither

### Create a `TaskEither`

```ts
// Build a TaskEither
const leftValue = taskEither.left('value');
const rightValue = taskEither.right('value');

// The taskEither module also provides similar builder functions
// to either, namely: `fromNullable`, `fromOption`, `fromPredicate`...

// Convert from Either
const eitherValue = either.right(42);

taskEither.fromEither(eitherValue); // => taskEither.right(42)

// Build from an async function
const asyncIsEven = async (n: number) => {
if (!isEven(n)) {
throw new Error(`${n} is odd`);
}

return n;
};

const buildTaskEither = (n: number) =>
taskEither.tryCatch(
() => asyncIsEven(n),
(error: Error) => error.message
);

buildTaskEither(3); // => taskEither.left('3 is odd')
buildTaskEither(4); // => taskEither.right(4)
```

#### Extract inner value

```ts
// Simple case: an infaillible Task
const someTask = task.of(42);

// Invoking a Task returns the underlying Promise
await someTask(); // => 42

// TaskEither
const someTaskEither = taskEither.right(42);
const eitherValue = await someTaskEither(); // => either.right(42)
either.getOrElse(() => 0)(eitherValue); // => 42

// Or more directly
const infaillibleTask = taskEither.getOrElse(() => 0)(someTaskEither); // => task.of(42)
await infaillibleTask(); // => 42
```

## ReadonlyArray

### Basic manipulation

```ts
const someArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

// filter and map
const result = pipe(
someArray,
readonlyArray.filter(isEven),
readonlyArray.map(square)
); // => [0, 4, 16, 36, 64]

// or in one go with filterMap
const result = pipe(
someArray,
readonlyArray.filterMap(
flow(option.fromPredicate(isEven), option.map(square))
)
);
```

### Sort elements with Ord

```ts
const strings = ['zyx', 'abc', 'klm'];

// basic sort
const sortedStrings = pipe(strings, readonlyArray.sort(string.Ord)); // => ['abc', 'klm', 'zyx']

// reverse sort
const reverseSortedStrings = pipe(
strings,
readonlyArray.sort(ord.reverse(string.Ord))
); // => ['zyx', 'klm', 'abc']

// sort Option
const optionalNumbers = [option.some(1337), option.none, option.some(42)];

const sortedNums = pipe(nums, readonlyArray.sort(option.getOrd(number.Ord))); // => [option.none, option.some(42), option.some(1337)]

// sort complex objects with different rules
type User = {
name: string;
age: Option;
};

const byName = pipe(
string.Ord,
ord.contramap((user: User) => user.name)
);

const byAge = pipe(
option.getOrd(number.Ord),
ord.contramap((user: User) => user.age)
);

const sortUsers = readonlyArray.sortBy([byAge, byName]); // will sort an array of users by age first, then by name
```

## Do-notation

```ts
import { readerTaskEither as rte } from 'fp-ts';
import { pipe } from 'fp-ts/function';
import { ReaderTaskEither } from 'fp-ts/ReaderTaskEither';

declare const foo: ReaderTaskEither;
declare const bar: ReaderTaskEither;
declare const baz: (props: {
foo: A1;
bar: A2;
}) => ReaderTaskEither;
declare const quux: (props: {
foo: A1;
bar: A2;
baz: A3;
}) => ReaderTask;
declare const transform: (props: { foo: A1; bar: A2; baz: A3 }) => B;

pipe(
rte.Do,
rte.apS('foo', foo),
rte.apSW('bar', bar),
rte.bindW('baz', baz),
rte.chainFirstReaderTaskKW(quux),
rte.map(transform)
);
// => ReaderTaskEither<
// R1 & R2 & R3 & R4,
// E1 | E2 | E3,
// B,
// >
```