What is fp-ts?
The fp-ts npm package is a library for functional programming in TypeScript. It provides developers with tools to write code in a functional style, leveraging concepts like type-safe functional combinators, monads, and other abstractions that enable expressive and concise code.
What are fp-ts's main functionalities?
Option Type
The Option type represents encapsulation of an optional value. A value of type Option<T> can either be some<T> if it exists or none if it does not. This is useful for handling cases where a value might be missing without resorting to null or undefined.
import { Option, some, none } from 'fp-ts/Option';
function find<T>(predicate: (a: T) => boolean, arr: T[]): Option<T> {
for (const item of arr) {
if (predicate(item)) {
return some(item);
}
}
return none;
}
const result = find(x => x > 10, [1, 2, 3]);
console.log(result); // output: none
Either Type
The Either type represents a value of one of two possible types (a disjoint union). An instance of Either is either an instance of left or right. It is useful for error handling where left can be used to hold an error value and right can hold a success value.
import { Either, left, right } from 'fp-ts/Either';
function divide(a: number, b: number): Either<string, number> {
return b === 0 ? left('Cannot divide by zero') : right(a / b);
}
const result = divide(10, 0);
console.log(result); // output: left('Cannot divide by zero')
IO Type
The IO type represents a computation that can perform side effects when executed. It is a way to manage side effects in a functional way by deferring their execution.
import { IO } from 'fp-ts/IO';
const log: IO<void> = () => console.log('Hello, fp-ts!');
log(); // output: 'Hello, fp-ts!'
Function Composition
Function composition is a core concept in functional programming, allowing you to combine multiple functions into a single function. The flow function from fp-ts helps you to compose functions easily.
import { flow } from 'fp-ts/function';
const toUpperCase = (s: string) => s.toUpperCase();
const exclaim = (s: string) => `${s}!`;
const shout = flow(toUpperCase, exclaim);
console.log(shout('hello')); // output: 'HELLO!'
Other packages similar to fp-ts
ramda
Ramda is a popular functional programming library for JavaScript. It emphasizes a functional style and provides many utilities for working with functions and data. Compared to fp-ts, Ramda is less focused on type safety and does not provide as many abstractions related to category theory.
sanctuary
Sanctuary is a functional programming library that provides type-safe functional data types and utility functions. It is similar to fp-ts in its emphasis on type safety and functional programming concepts, but it has its own set of abstractions and API design choices.
A mix of
See the section "Technical overview" below for an explanation of the technique.
Documentation
Algebraic types
| Array | Option | Either | NEA(*) | Task | Const | Identity | Validation |
---|
Setoid | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ |
Semigroup | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
Monoid | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
Functor | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Contravariant | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
PointedFunctor | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Apply | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Applicative | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Alt | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
Plus | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
Alternative | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
Foldable | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ |
Traversable | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ |
Chain | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ |
ChainRec | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
Extract | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
Extend | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ |
Comonad | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
Bifunctor | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
(*) NonEmptyArray
Monads
- Array
- Either
- Identity
- Option
- Reader
- State
- Task
- Writer
Comonads
Monad transformers
Technical overview
Higher kinded types and type classes
Higher kinded types are represented by a unique string literal (called URI
).
There's a central type dictionary where a mapping URI
-> concrete type is stored
export interface HKT<A> {}
Instances can be defined (everywhere) using a feature called Module Augmentation so there's no danger of name conflict (the typechecker checks for duplicates).
Here's a mapping between the string literal 'Option'
and the concrete type Option<A>
declare module './HKT' {
interface HKT<A> {
Option: Option<A>
}
}
export const URI = 'Option'
export type URI = typeof URI
export type Option<A> = None<A> | Some<A>
export class None<A> {
readonly _URI: URI
map<B>(f: (a: A) => B): Option<B> {
return none
}
...
}
export Some<A> {
readonly _URI: URI
map<B>(f: (a: A) => B): Option<B> {
return new Some(f(this.value))
}
...
}
export function map<A, B>(f: (a: A) => B, fa: Option<A>): Option<B> {
return fa.map(f)
}
Concrete types can be retrieved by their URI
using a feature called Index types.
Type classes are implemented following (when possible) both the static-land spec and the fantasy-land spec.
Here's the definition of the type class Functor
(following the static-land spec)
export interface StaticFunctor<F extends HKTS> {
URI: F
map<A, B>(f: (a: A) => B, fa: HKT<A>[F]): HKT<B>[F]
}
License
The MIT License (MIT)