@tigerabrodioss/berwald
v2.0.0
Published
A fun functional programming library written in TypeScript
Maintainers
Readme
Berwald
Berwald is a functional programming library for TypeScript. I built it for fun and to learn more about functional programming.
Installation
# Using npm
npm install @tigerabrodioss/berwald
# Using yarn
yarn add @tigerabrodioss/berwald
# Using pnpm
pnpm add @tigerabrodioss/berwaldOverview
Berwald provides a comprehensive set of tools for functional programming in TypeScript:
- Core data types: Option, Either, List, Tuple
- Pattern matching: Expressive pattern matching syntax
- Higher-order functions: Utilities for working with functions
- Type classes: Semigroup, Monoid, Group
- Utilities: Pipe, Compose, Curry
All modules are exported as namespaces to prevent name collisions.
Examples
Option: Handling nullable values
import { Option } from '@tigerabrodioss/berwald'
// Create options
const greeting = Option.some('Hello')
const empty = Option.none
// Transform values safely
const uppercased = Option.map((s: string) => s.toUpperCase())(greeting) // Some('HELLO')
// Handle missing values with a default
const value = Option.getOrElse('Default')(uppercased) // 'HELLO'
const fallback = Option.getOrElse('Default')(empty) // 'Default'Either: Error handling
import { Either, TryCatch } from '@tigerabrodioss/berwald'
// Parse JSON safely
const parseJSON = TryCatch.tryCatch(
JSON.parse,
(error: unknown) => `Invalid JSON: ${String(error)}`
)
const result = parseJSON('{"name":"Alice"}') // Right({ name: 'Alice' })
const error = parseJSON('invalid') // Left('Invalid JSON: ...')
// Process result
if (Either.isRight(result)) {
console.log(result.right.name) // 'Alice'
} else {
console.error(result.left) // Error message
}List: Immutable list operations
import { List } from '@tigerabrodioss/berwald'
// Create a list
const numbers = List.fromArray([1, 2, 3, 4, 5])
// Transform list contents
const doubled = List.map((n: number) => n * 2)(numbers)
const evens = List.filter((n: number) => n % 2 === 0)(numbers)
// Convert back to array
const result = List.toArray(doubled) // [2, 4, 6, 8, 10]Pattern Matching
import { Match } from '@tigerabrodioss/berwald'
type Shape =
| { type: 'circle'; radius: number }
| { type: 'rectangle'; width: number; height: number }
| { type: 'triangle'; base: number; height: number }
const calculateArea = (shape: Shape) =>
Match.match(shape)(
Match.when(
(s) => s.type === 'circle',
(s) => Math.PI * (s as { radius: number }).radius ** 2
),
Match.when(
(s) => s.type === 'rectangle',
(s) =>
(s as { width: number; height: number }).width *
(s as { width: number; height: number }).height
),
Match.when(
(s) => s.type === 'triangle',
(s) =>
((s as { base: number; height: number }).base *
(s as { base: number; height: number }).height) /
2
)
)Function Composition
import { Pipe, Compose } from '@tigerabrodioss/berwald'
const add = (a: number) => (b: number) => a + b
const multiply = (a: number) => (b: number) => a * b
const toString = (a: number) => `Result: ${a}`
// Pipe: left-to-right composition
const calculate1 = Pipe.pipe(
5,
add(10), // 15
multiply(2), // 30
toString // "Result: 30"
)
// Compose: right-to-left composition
const processNumber = Compose.compose(toString, multiply(2), add(10))
const calculate2 = processNumber(5) // "Result: 30"API Reference
Core Modules
Option
Represents values that may or may not exist.
some<A>(value: A): Option<A>- Creates a Some containing a valuenone: Option<never>- Represents no valueisSome<A>(option: Option<A>): option is Some<A>- Type guard for SomeisNone<A>(option: Option<A>): option is None- Type guard for Nonemap<A, B>(f: (a: A) => B): (option: Option<A>) => Option<B>- Maps the contained valueflatMap<A, B>(f: (a: A) => Option<B>): (option: Option<A>) => Option<B>- Maps and flattensgetOrElse<A>(defaultValue: A): (option: Option<A>) => A- Gets value or returns default
Either
Represents a value of one of two possible types (a disjoint union).
right<E, A>(value: A): Either<E, A>- Creates a Right (success) valueleft<E, A>(value: E): Either<E, A>- Creates a Left (error) valueisRight<E, A>(either: Either<E, A>): either is Right<A>- Type guard for RightisLeft<E, A>(either: Either<E, A>): either is Left<E>- Type guard for Leftmap<E, A, B>(f: (a: A) => B): (either: Either<E, A>) => Either<E, B>- Maps Right values
List
An immutable linked list implementation.
nil: List<never>- The empty listcons<A>(head: A, tail: List<A>): List<A>- Creates a new listfromArray<A>(arr: ReadonlyArray<A>): List<A>- Creates a list from an arraytoArray<A>(list: List<A>): ReadonlyArray<A>- Converts a list to an arraymap<A, B>(f: (a: A) => B): (list: List<A>) => List<B>- Maps over list items
Pattern Matching
match<A>(value: A)- Starts a pattern match expressionwhen<A, B>(predicate: (a: A) => boolean, transform: (a: A) => B)- Defines a match patternotherwise<A, B>(transform: (a: A) => B)- Default case for when no pattern matches
Utilities
pipe<A, B, ...>(a: A, ab: (a: A) => B, ...): ...- Pipes a value through functionsflow<A, B, ...>(ab: (a: A) => B, ...): (a: A) => ...- Creates a pipeline functioncurry<A, B, ...>(f: (a: A, b: B, ...) => R): (a: A) => (b: B) => ... => R- Curries a function
License
MIT © Tiger Abrodi
