Skip to content

nand-industries/monads.ts

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

A minimal functional utility library for TypeScript, inspired by Rust's Result and Option. It provides safe, composable handling of errors and optional values.

  • Result<T> – represents a computation that may succeed (ok) or fail (err).
  • Option<T> / Maybe<T> – represents a value that may exist (Some) or be absent (None).
  • SafePromise<T> – wraps async computations, converting exceptions into Result<T>.
  • Supports chainable operations: map, andThen, filter, zip, transpose, mapErr, or, orElse, unwrap, expect, inspect.

Goals:

  1. Explore functional programming in TypeScript.
  2. Provide a small, dependency-free, composable monads toolkit.
  3. Enable safe handling of errors and optional values without exceptions.

Status: eperimental. API may change. contributions welcome.

Installation

bun add @nand.computer/monads
import { ok, err, Some, None, wrap, todo } from "@nand.computer/monads";

Result

Result<T> is either:

  • ok(value: T) – success
  • err(message: string) – failure

Methods

  • .map(fn) – transform success value
  • .andThen(fn) – chain computations
  • .mapErr(fn) – transform the error
  • .filter(predicate, errMsg) – fail if predicate is false
  • .zip(otherResult) – combine two results
  • .transpose() – convert Result<Option<T>>Option<Result<T>>
  • .unwrap() – return value or throw
  • .expect(msg) – return value or throw with custom message
  • .ok() – returns the value or null
  • .err() – returns the error message or null

Option / Maybe

Option<T> represents an optional value:

  • Some(value) – value exists
  • None() – value absent

Methods

  • .map(fn) – transform value
  • .andThen(fn) – chain computations
  • .unwrap() – return value or throw
  • .expect(msg) – return value or throw with message
  • .filter(predicate) – convert to None if predicate fails

SafePromise

Wraps async functions to automatically return Result<T>:

const safe = await wrap(async () => 10 * 2);
console.log(safe.ok()); // 20

Examples

// Basic map/andThen/mapErr
const r1 = ok(10)
  .map((x) => x * 2)
  .andThen((x) => (x > 15 ? ok(x) : err<number>("Too small")))
  .mapErr((e) => `Error happened: ${e}`);
console.log(r1.ok(), r1.err()); // 20, null

// Filter
const filtered = ok(100).filter((x) => x > 50, "Less than 50");
console.log(filtered.ok(), filtered.err()); // 100, null

// Zip
const zipped = ok(10).zip(ok("hello"));
console.log(zipped.ok()); // [10, "hello"]

// Option + transpose
const rOpt: Result<Option<number>> = ok(Some(5));
const transposed = rOpt.transpose();
console.log(transposed.isSome ? transposed.value!.ok() : null); // 5

const rOptNone: Result<Option<number>> = ok(None());
const transposedNone = rOptNone.transpose();
console.log(transposedNone.isNone); // true

// SafePromise / wrap
const asyncFunc = async (x: number) =>
  x > 0
    ? x * 2
    : (() => {
        throw new Error("Negative");
      })();
const safe = await wrap(() => asyncFunc(5));
console.log(safe.ok()); // 10
const safeErr = await wrap(() => asyncFunc(-1));
console.log(safeErr.err()); // "Unexpected error"

// Complex chaining
const complex = await wrap(async () => 20).then((r) =>
  r
    .filter((x) => x > 10, "Too small")
    .andThen((x) => ok(x * 3))
    .zip(ok(50))
    .map(([a, b]) => a + b),
);
console.log(complex.ok()); // 110

Goals

  1. Learn, experiment, and have fun with functional patterns.
  2. Provide a safe, composable substrate for async and optional computations.
  3. Maintain simplicity and extensibility.

About

A minimal monads in typescript

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • TypeScript 100.0%