DeutschGitHub
partialpackages/functional/src

Functional

@ralphschuler/functional

Exports tagged Option and Result values, guards and pattern matching.

OptionResultdiscriminated unions

01 · Problem

What is it for?

null and thrown exceptions often mix absent data with failed computation.

02 · Mental model

How should you think about it?

A tag identifies the valid branch. Match the branch before reading its value or error.

In this repository

Exports tagged Option and Result values, guards and pattern matching. Wrappers are shallowly frozen; error capture and higher-order combinators are explicit extensions.

03 · Control flow

What happens in which order?

  1. Wrap a present value or absence.
  2. Return Ok or Err for expected outcomes.
  3. Narrow by the tag or a guard.
  4. Handle each branch or provide a fallback.

04 · Building blocks

The important contracts

Some / None / Ok / ErrConstruct immutable tagged wrappers.
isSome / isNone / isOk / isErr / matchNarrow and handle each variant.

05 · Build it yourself

Implement a small core

Start with this small independent implementation, then use the steps below to make its contract explicit.

  1. Wrap a present value or absence.
  2. Return Ok or Err for expected outcomes.
  3. Narrow by the tag or a guard.
  4. Handle each branch or provide a fallback.
Concept sketch · may reference omitted helpers
type Result<T, E> =
  | { tag: "ok"; value: T }
  | { tag: "err"; error: E };

const ok = <T>(value: T): Result<T, never> => ({ tag: "ok", value });
const err = <E>(error: E): Result<never, E> => ({ tag: "err", error });

function map<A, B, E>(result: Result<A, E>, fn: (value: A) => B): Result<B, E> {
  return result.tag === "ok" ? ok(fn(result.value)) : result;
}

06 · Verify

What should you test?

  • Preserve 0, false and empty strings as present values.
  • Test each tag and matcher fallback.
  • Verify wrappers are frozen without assuming deep immutability.

Try it yourself

Turn the first verification claim into a failing test, implement the smallest fix, then add a boundary case.

Preserve 0, false and empty strings as present values.

Compare your approach

Your check should fail when the stated invariant is deliberately broken, and pass again after the fix. Keep setup deterministic and assert an observable result.

07 · Limits

Trade-offs and failure modes

  • A Result does not automatically catch exceptions.
  • Nested values remain mutable.
  • Choose consistent error types across a pipeline.
Important

Wrappers are shallowly frozen; error capture and higher-order combinators are explicit extensions.

08 · Keep exploring

Source code and related ideas

yarn tsx packages/functional/examples/basic.ts