Skip to content

smullyanCombinatory logic, fully typed

The bird combinators of To Mock a Mockingbird, plus Option, Result, Task and Reader — curried, tree-shakable, and zero-dependency.

At a glance

ts
import { B, C, K, S, Y } from 'smullyan/birds';
import { pipe } from 'smullyan/pipe';
import * as Option from 'smullyan/option';

// Composition — the Bluebird
const incThenShow = B(String)((n: number) => n + 1);
incThenShow(41); // '42'

// Recursion without a name — the Sage bird
const factorial = Y<number, number>((rec) => (n) => (n <= 1 ? 1 : n * rec(n - 1)));
factorial(5); // 120

// Pipelines
pipe(
  Option.fromNullable(process.env.PORT),
  Option.map(Number),
  Option.filter((n: number) => Number.isInteger(n)),
  Option.getOrElse(() => 3000),
);

Install

sh
pnpm add smullyan
sh
npm install smullyan
sh
bun add smullyan

Entry points

Every module is side-effect free and independently importable, so you ship only what you use.

ImportContains
smullyanThe birds, plus pipe and flow
smullyan/birdsAll thirty-six combinators
smullyan/pipepipe and flow
smullyan/optionOption<A>
smullyan/resultResult<E, A>
smullyan/taskTask<A>, TaskResult<E, A>
smullyan/readerReader<R, A>

The ADTs are not re-exported from the root: each defines map, flatMap, match and getOrElse, so they would collide. Import them by subpath.

MIT licensed.