A functional programming library for Rust featuring your favourite higher-kinded types and type classes.
- HKT emulation in stable Rust via type-level defunctionalization.
- Type class hierarchy inspired by PureScript / Haskell (
Functor,Monad,Foldable, etc.). - Brand inference:
map(|x| x + 1, Some(5))with no turbofish needed. - Val/Ref dispatch: one function handles both owned and borrowed containers.
- Zero-cost core operations (map, bind, fold, etc.) via static dispatch.
- Works with
stdtypes (Option,Result,Vec, etc.). - Advanced features: optics, lazy evaluation, parallel traits.
Rust is a multi-paradigm language with strong functional programming features like iterators, closures, and algebraic data types. However, it lacks native support for Higher-Kinded Types (HKT), which limits the ability to write generic code that abstracts over type constructors (e.g., writing a function that works for any Monad, whether it's Option, Result, or Vec). fp-library aims to bridge this gap.
The brand is inferred automatically from the container type:
use fp_library::functions::*;
fn main() {
// Brand inferred from Option<i32>
let y = map(|i: i32| i * 2, Some(5));
assert_eq!(y, Some(10));
// Brand inferred from &Vec<i32> (by-reference dispatch)
let v = vec![1, 2, 3];
let y = map(|i: &i32| *i + 10, &v);
assert_eq!(y, vec![11, 12, 13]);
}For types with multiple brands (e.g., Result, which can be viewed as a functor over
either its Ok or Err type), use the explicit variant to select the brand:
use fp_library::{brands::*, functions::explicit::*};
fn main() {
// ResultErrAppliedBrand fixes the error type, so map operates on the Ok value.
let y = map::<ResultErrAppliedBrand<&str>, _, _, _, _>(|i| i * 2, Ok::<i32, &str>(5));
assert_eq!(y, Ok(10));
}The m_do! macro provides Haskell/PureScript-style do-notation for flat monadic code.
It desugars <- binds into nested bind calls.
use fp_library::{brands::*, functions::*, m_do};
fn main() {
// Inferred mode: brand inferred from container types
let result = m_do!({
x <- Some(5);
y <- Some(x + 1);
let z = x * y;
Some(z)
});
assert_eq!(result, Some(30));
// Explicit mode: for ambiguous types or when pure() is needed
let result = m_do!(VecBrand {
x <- vec![1, 2];
y <- vec![10, 20];
pure(x + y)
});
assert_eq!(result, vec![11, 21, 12, 22]);
}Add fp-library to your Cargo.toml:
[dependencies]
fp-library = "0.17"The published 0.17 release line does not include the effects feature or its API. The effects material below and the linked effects guides describe the current unreleased repository source; use a repository checkout to evaluate that surface until it is published.
For a detailed breakdown of all features, type class hierarchies (with Mermaid diagrams), data types, and macros, see the Features documentation.
The library offers optional features that can be enabled in your Cargo.toml:
rayon: Enables true parallel execution forpar_*functions using the rayon library. Without this feature,par_*functions fall back to sequential equivalents.serde: Enables serialization and deserialization support for pure data types using the serde library.stacker: Enables adaptive stack growth for deepCoyonedamap chains (at every store) via the stacker crate. Without this feature, deeply chained maps can overflow the stack.effects: In the current unreleased repository source, enables the optional, experimental unified-row effects subsystem. It provides the publicdefine_effect!anddefine_row!macros, one-pass and narrowing interpretation, an Rc-pinned forking tier, and seven public built-in effects (State,Writer,Choose,Alt,Coroutine,Shift, andSubShift). The other thirteen catalog effects remain crate-internal reference fixtures. The Arc/Send marked-operation and forking tier is deferred. This feature is not present in the published 0.17 release line, and the API may change before release.
To enable features:
[dependencies]
# Single feature
fp-library = { version = "0.17", features = ["rayon"] }
# Multiple features
fp-library = { version = "0.17", features = ["rayon", "serde"] }Higher-Kinded Types: The library encodes HKTs using lightweight higher-kinded polymorphism (the "Brand" pattern). Each type constructor has a zero-sized brand type (e.g., OptionBrand) that implements Kind traits mapping brands back to concrete types. See Higher-Kinded Types.
Dispatch System: Free functions like map and bind infer the brand from the container type and route to by-value or by-reference trait methods automatically, so most call sites need no turbofish. For details, see Brand Inference and Val/Ref Dispatch.
Effects: In the current unreleased source, the experimental effects subsystem represents effectful programs as data on the crate's Free substrate: one unified type-level row of effect brands, higher-order effects elaborated over that row, and brand-keyed dispatch. Public programs are defined with define_effect! and define_row! and interpreted through emitted #[handlers] APIs, narrowing runners, async await_future / run_async, or the Rc-pinned multi-shot forking surface. The built-in catalog contains seven public, payload-generalised effects and thirteen crate-internal reference fixtures. The Arc/Send forking tier remains deferred. Requires the source-only effects crate feature; published 0.17 crates do not contain this surface.
Zero-Cost Abstractions: Core operations use uncurried semantics with impl Fn for static dispatch and zero heap allocation. Dynamic dispatch (dyn Fn) is reserved for cases where functions must be stored as data. See Zero-Cost Abstractions.
Lazy Evaluation: A granular hierarchy of lazy types (Thunk, Trampoline, Lazy) lets you choose trade-offs between stack safety, memoization, lifetimes, and thread safety. Each has a fallible Try* counterpart. See Lazy Evaluation.
Thread Safety & Parallelism: A parallel trait hierarchy (ParFunctor, ParFoldable, etc.) mirrors the sequential one. When the rayon feature is enabled, par_* functions use true parallel execution. See Thread Safety and Parallelism.
- API Documentation: The published crate API reference on docs.rs; the published 0.17 line does not contain the unreleased effects surface.
- Features & Type Class Hierarchy: Full feature list with hierarchy diagrams.
- Higher-Kinded Types: The Brand pattern and HKT encoding.
- Brand Inference: Brand inference, trait shapes, Marker invariant, and inference resolution.
- Val/Ref Dispatch: Unified by-value and by-reference function dispatch.
- Zero-Cost Abstractions: Uncurried semantics and static dispatch.
- Pointer Abstraction: Pointer hierarchy,
FnBrand<P>, and shared memoization. - Lazy Evaluation: Guide to the lazy evaluation and memoization types.
- Coyoneda Implementations: Trade-offs between the
Store-parameterisedCoyonedadesign andCoyonedaExplicit. - Effects System: Design, public runner tiers, and the built-in reference catalog in the current unreleased source.
- Custom Effects: Defining rows and effects and interpreting them with the current unreleased source API.
- Thread Safety & Parallelism: Parallel trait hierarchy and rayon support.
- Limitations and Workarounds: Rust type system constraints and how the library addresses them.
- Project Structure: Module layout and dependency graph.
- Architecture & Design: Design decisions and documentation conventions.
- Optics Analysis: Optics coverage comparison with PureScript.
- Profunctor Analysis: Profunctor class hierarchy comparison with PureScript.
- Std Library Coverage: Type class coverage for standard library types.
- Benchmarks: Performance results, graphs, and benchmark coverage.
- References: Papers, libraries, and resources that informed this project.
We welcome contributions!
To get started:
- Check out our Contributing Guide for environment setup and development workflows.
- Read the documentation files to get a high-level understanding of the project.
- Join the conversation in GitHub Issues.
Please ensure all PRs pass just verify before submission.
This project is licensed under the Blue Oak Model License 1.0.0.
See References for papers, libraries, and other resources that informed this project.