Option and Result
Use Option<A> when a value may be absent. Use Result<A, E> when a
recoverable operation can explain why it failed. Both make the unhappy path
part of the type instead of encoding it as null, an exception, or a
success-flag convention.
import * as O from '@stopcock/fp/option'import * as R from '@stopcock/fp/result'Option
Section titled “Option”type None = { readonly _tag: 0 }type Some<A> = { readonly _tag: 1; readonly value: A }type Option<A> = None | Some<A>Create options at nullable and predicate boundaries:
const portText = O.fromNullable(process.env.PORT)const positive = O.fromPredicate((value: number) => value > 0)(3)Then transform without repeatedly branching:
import { pipe } from '@stopcock/fp'
const port = pipe( portText, O.map(Number), O.filter((value) => Number.isInteger(value) && value > 0), O.getOrElse(() => 3000),)mapNullable, flatMap, filter, zip, all, struct, and traverse
cover the common composition shapes. Partial extraction is explicit:
const label = O.match({ none: () => 'not configured', some: (value) => `port ${value}`,})(portText)none is a frozen singleton. The representation uses numeric tags: 0 is
None, 1 is Some.
Result
Section titled “Result”type Err<E> = { readonly _tag: 0; readonly error: E }type Ok<A> = { readonly _tag: 1; readonly value: A }type Result<A, E> = Ok<A> | Err<E>Translate throwing and nullable APIs at the boundary:
type ParseError = { readonly type: 'InvalidJson' readonly cause: unknown}
const parsed = R.tryCatch( () => JSON.parse(input) as unknown, (cause): ParseError => ({ type: 'InvalidJson', cause }),)
const token = R.fromNullable(() => 'Missing token')( headers.authorization,)map transforms success. mapErr transforms failure. flatMap sequences
dependent work and keeps the union of possible error types.
const response = pipe( parsed, R.flatMap(decodeRequest), R.flatMap(authorizeRequest), R.match({ err: (error) => ({ status: 400, error }), ok: (value) => ({ status: 200, value }), }),)all, struct, and traverse are fail-fast and preserve tuple/object types.
Use Validation when independent checks must all run and
accumulate errors.
Conversion
Section titled “Conversion”O.toResult(() => 'missing')(option)R.toOption(result) // intentionally discards the errorO.transpose(optionOfResult)Asynchronous failure belongs in Task from @stopcock/async. Keeping
Option, Result, and Task separate makes evaluation and cancellation
semantics visible.