Purify TS: Functional Programming Standard Library

2.1.4 · active · verified Sun Apr 19

Purify is a TypeScript library providing a standard set of functional programming abstractions, primarily focused on Algebraic Data Types (ADTs) such as Maybe, Either, and their asynchronous counterparts. Its purpose is to enable developers to write safer, more maintainable code by handling optional values and errors explicitly. The current stable version is 2.1.4, with patch releases addressing build issues and minor fixes occurring frequently, while minor versions introduce new features. Purify distinguishes itself by offering a developer-friendly API that prioritizes practical application and ease of use over more complex theoretical constructs like Higher-Kinded Types, a deliberate design choice given TypeScript's current capabilities. It is fully type-safe, entirely written in TypeScript, and adheres to the Fantasy Land specification, aiming to seamlessly integrate robust error and optionality handling into existing TypeScript projects without requiring elaborate type definitions or runtime workarounds.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates the core usage of Purify-TS's Maybe and Either ADTs for safely handling optional values and propagating errors, respectively. It also shows `fromPredicate` for type-safe validation.

import { Maybe, Either, Just, Nothing, Left, Right } from 'purify-ts';

// Example 1: Handling optional values with Maybe
function getUserEmail(id: number): Maybe<string> {
  const users = {
    1: { name: 'Alice', email: 'alice@example.com' },
    2: { name: 'Bob', email: undefined },
    3: { name: 'Charlie', email: 'charlie@example.com' }
  };
  const user = users[id];
  return Maybe.fromNullable(user?.email); // Safely get email, handles null/undefined
}

console.log(`User 1 email: ${getUserEmail(1).map(e => e.toUpperCase()).getOrElse('No email found')}`);
// Expected: User 1 email: ALICE@EXAMPLE.COM

console.log(`User 2 email: ${getUserEmail(2).map(e => e.toUpperCase()).getOrElse('No email found')}`);
// Expected: User 2 email: No email found

console.log(`User 4 email: ${getUserEmail(4).map(e => e.toUpperCase()).getOrElse('No email found')}`);
// Expected: User 4 email: No email found

// Example 2: Working with Either for success/failure
function divide(a: number, b: number): Either<string, number> {
  return b === 0 ? Left('Cannot divide by zero') : Right(a / b);
}

const result1 = divide(10, 2);
result1.ifRight(val => console.log(`10 / 2 = ${val}`)); // Expected: 10 / 2 = 5
result1.ifLeft(err => console.error(`Error: ${err}`));

const result2 = divide(10, 0);
result2.ifRight(val => console.log(`10 / 0 = ${val}`));
result2.ifLeft(err => console.error(`Error: ${err}`)); // Expected: Error: Cannot divide by zero

// Example 3: Using fromPredicate with type guards
const isPositive = (num: number): num is number => num > 0;
const maybePositive = Maybe.fromPredicate(isPositive, 5);
console.log(`Is 5 positive? ${maybePositive.isJust()}`); // Expected: Is 5 positive? true

const maybeNegative = Maybe.fromPredicate(isPositive, -1);
console.log(`Is -1 positive? ${maybeNegative.isJust()}`); // Expected: Is -1 positive? false

view raw JSON →