@typesugar/fp
v0.1.0
Published
๐ง Functional programming library with typeclasses and data types for typesugar
Maintainers
Readme
@typesugar/fp
Functional programming library for TypeScript, inspired by Scala's Cats library.
Overview
@typesugar/fp provides a complete functional programming toolkit: typeclasses (Functor, Monad, Applicative), data types (Option, Either, List, Validated), monad transformers (State, Reader, Writer), and an IO monad with stack-safe interpreter.
Installation
npm install @typesugar/fp
# or
pnpm add @typesugar/fpQuick Start
import { Option, Some, None, Either, Left, Right, IO, runIO, pipe, flow } from "@typesugar/fp";
// Option โ nullable values
const user = Option.flatMap(Some(2), (x) => Some(x * 3));
// Some(6)
// Either โ error handling
const result = Either.map(Right(42), (x) => x.toString());
// Right("42")
// IO โ pure effects
const program = IO.flatMap(
IO.delay(() => "Hello"),
(msg) => IO.delay(() => console.log(msg))
);
await runIO(program);
// Pipe โ function composition
const transformed = pipe(
5,
(x) => x * 2,
(x) => x + 1,
(x) => x.toString()
);
// "11"Data Types
Option
Represents optional values โ Some<A> or None.
import { Option, Some, None, isSome, isNone } from "@typesugar/fp";
const value = Some(42);
const empty = None<number>();
Option.map(value, (x) => x * 2); // Some(84)
Option.flatMap(value, (x) => Some(x + 1)); // Some(43)
Option.getOrElse(empty, 0); // 0
Option.filter(value, (x) => x > 50); // NoneEither<L, R>
Represents success (Right) or failure (Left).
import { Either, Left, Right, isLeft, isRight } from "@typesugar/fp";
const success = Right<string, number>(42);
const failure = Left<string, number>("error");
Either.map(success, (x) => x * 2); // Right(84)
Either.mapLeft(failure, (e) => e.toUpperCase()); // Left("ERROR")
Either.fold(
success,
(e) => 0,
(x) => x
); // 42List
Immutable linked list.
import { List, Cons, Nil } from "@typesugar/fp";
const list = List.of(1, 2, 3);
List.map(list, (x) => x * 2); // [2, 4, 6]
List.filter(list, (x) => x > 1); // [2, 3]
List.foldLeft(list, 0, (a, b) => a + b); // 6Validated<E, A>
Accumulates errors instead of failing fast.
import { Validated, valid, invalid, validNel, invalidNel } from "@typesugar/fp";
const v1 = valid<string[], number>(42);
const v2 = invalid<string[], number>(["error 1"]);
const v3 = invalid<string[], number>(["error 2"]);
// Combine with error accumulation
Validated.mapN(v2, v3, (a, b) => a + b);
// Invalid(["error 1", "error 2"])IO
Pure description of side effects with stack-safe interpreter.
import { IO, runIO, runIOSync } from "@typesugar/fp";
const program = IO.flatMap(
IO.delay(() => fetch("/api/user")),
(response) => IO.delay(() => response.json())
);
// Run the effect
const result = await runIO(program);Monad Transformers
State<S, A>
Stateful computations.
import { State, IndexedState } from "@typesugar/fp";
const increment = State.modify<number>((n) => n + 1);
const getDouble = State.gets<number, number>((n) => n * 2);
const program = State.flatMap(increment, () => getDouble);
State.run(program, 5); // [12, 6] (new state, result)Reader<R, A>
Dependency injection.
import { Reader, Kleisli } from "@typesugar/fp";
interface Config {
apiUrl: string;
}
const getUrl = Reader.ask<Config>().map((c) => c.apiUrl);
const fetchData = Reader.flatMap(getUrl, (url) => Reader.pure(fetch(url)));
Reader.run(fetchData, { apiUrl: "http://api.example.com" });Writer<W, A>
Logging and accumulation.
import { Writer, LogWriter } from "@typesugar/fp";
const program = Writer.flatMap(Writer.tell(["Started"]), () =>
Writer.flatMap(Writer.pure(42), (x) => Writer.tell([`Got ${x}`]).map(() => x))
);
Writer.run(program); // [["Started", "Got 42"], 42]Typeclasses
import { TC } from "@typesugar/fp";
// Functor
TC.Functor.map(someOption, f);
// Applicative
TC.Applicative.pure(42);
TC.Applicative.ap(Some(f), Some(x));
// Monad
TC.Monad.flatMap(effect, f);
// Foldable
TC.Foldable.foldLeft(list, init, f);
// Traverse
TC.Traverse.traverse(list, f);Zero-Cost Abstractions
Compile-time-optimized variants of common functional patterns with zero runtime overhead.
import {
type ZeroCostOption,
ZeroCostOptionOps,
type ZeroCostResult,
ZeroCostResultOps,
match,
matchLiteral,
matchGuard,
} from "@typesugar/fp";
// Zero-cost Option โ just T | null at runtime
const opt: ZeroCostOption<number> = 42;
if (ZeroCostOptionOps.isSome(opt)) {
console.log(opt * 2);
}
// Zero-cost Result โ minimal object wrapper
const result = ZeroCostResultOps.ok<number, string>(42);
if (ZeroCostResultOps.isOk(result)) {
console.log(result.value);
}
// Pattern matching (compiles to if/else chains)
type Shape = { kind: "circle"; radius: number } | { kind: "square"; side: number };
const area = match(shape, {
circle: (s) => Math.PI * s.radius ** 2,
square: (s) => s.side * s.side,
});See also: @typesugar/fp/zero-cost for direct imports.
Syntax Utilities
import { pipe, flow } from "@typesugar/fp";
// Left-to-right application
const result = pipe(
5,
(x) => x * 2,
(x) => x + 1
);
// Function composition
const transform = flow(
(x: number) => x * 2,
(x) => x + 1,
(x) => x.toString()
);API Reference
Data Types
Option,Some,Noneโ Optional valuesEither,Left,Rightโ Error handlingList,Cons,Nilโ Immutable listsNonEmptyListโ Non-empty listsValidated,Valid,Invalidโ Error accumulationState,Reader,Writerโ Monad transformersIOโ Effect monad
Typeclasses (TC namespace)
Functor,Apply,ApplicativeFlatMap,MonadFoldable,TraverseSemigroup,MonoidEq,Ord,ShowApplicativeError,MonadError
IO Runtime
runIO(io)โ Execute asyncrunIOSync(io)โ Execute syncIODoโ Do-notation helper
License
MIT
