Skip to content

Instantly share code, notes, and snippets.

@kayac-chang
Created January 28, 2022 11:53
Show Gist options
  • Save kayac-chang/06fdc280357af48f4f68e8af675f27f9 to your computer and use it in GitHub Desktop.
Save kayac-chang/06fdc280357af48f4f68e8af675f27f9 to your computer and use it in GitHub Desktop.
[learn FP with Kirby using `fp-ts`] Option
import { describe } from "../utils";
/**
* chain (flatmap)
* ===
* the chain can perform `map` and `flatten` together.
*/
import { flow, pipe } from "fp-ts/lib/function";
import * as Option from "fp-ts/Option";
interface Fizz {
buzz: string;
}
interface Foo {
bar?: Fizz;
}
const foo: Foo = { bar: undefined };
describe(`Solution. using chain (flatmap) to improve nested nullable call`, () =>
pipe(
foo,
Option.fromNullable,
Option.map(({ bar }) => bar),
Option.chain(
flow(
Option.fromNullable,
Option.map(({ buzz }) => buzz)
)
)
));
import { describe } from "../utils";
/**
* flatten
* ===
* handle where object has sequentially nested nullable properties.
*
*/
import { pipe } from "fp-ts/lib/function";
import * as Option from "fp-ts/Option";
interface Fizz {
buzz: string;
}
interface Foo {
bar?: Fizz;
}
const foo: Foo = { bar: undefined };
describe(`Question. try to solve this by nested fromNullable`, () =>
pipe(
foo,
Option.fromNullable,
Option.map(({ bar }) =>
pipe(
bar,
Option.fromNullable,
Option.map(({ buzz }) => buzz)
)
) // { _tag: 'Some', value: { _tag: 'None' } }
));
describe(`Solution. flatten to recure`, () =>
pipe(
foo,
Option.fromNullable,
Option.map(({ bar }) =>
pipe(
bar,
Option.fromNullable,
Option.map(({ buzz }) => buzz)
)
),
Option.flatten
// { _tag: 'None' }
));
/**
* `flatten` take the last `Option` in the pipeline.
*/
describe(`if we wanted to check result 'Option' was 'Some' or 'None'`, () =>
pipe(
foo,
Option.fromNullable,
Option.map(({ bar }) =>
pipe(
bar,
Option.fromNullable,
Option.map(({ buzz }) => buzz)
)
),
Option.flatten,
Option.isSome // false
// Option.isNone // true
));
import { pipe } from "fp-ts/lib/function";
import { describe } from "../utils";
/**
* Option (Maybe)
* ===
* Option are containers that wrap values that could be either `undefined` or `null`.
*
* if the value exists,
* we say the Option is of the `Some` type.
* if the value is `undefined` or `null`,
* we say it has the `None` type.
*/
import * as Option from "fp-ts/Option";
type Fn<A, B> = (a: A) => B;
interface Foo {
bar: string;
}
const maybeGetFoo: Fn<void, Foo | undefined> = () =>
Math.random() < 0.5 ? { bar: "hello" } : undefined;
describe(`Example 1. handle the possibly undefined with optional chaining`, () =>
pipe(maybeGetFoo(), (f) => f?.bar)); // hello
describe(`Example 2. we want to avoid introduce variable 'f', but we got type error`, () =>
pipe(maybeGetFoo(), ({ bar }) => bar)); //! type error
/**
* Why should we use Option instead of `optional chaining` or `nullish coalescing`.
*
* below introduce why Option more powerful than build-in.
*/
describe(`Solution 1. Option to rescue with 'map' function`, () =>
pipe(
maybeGetFoo(),
Option.fromNullable,
Option.map(({ bar }) => bar)
));
/**
* if `maybeGetFoo`
* return Foo result will be { _tag: 'Some', value: 'hello' }
* return undefined result will be { _tag: 'None' }
*/
/**
* How does `Option.map` work over the `Option` ?
*
* It works by performing a comparison over the `_tag` property,
* if the `_tag` is `Some`, it transforms the value with function passed in `map`,
* otherwise,
* if the `_tag` is `None`, no operation is performed.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment