| Crates.io | fp-library |
| lib.rs | fp-library |
| version | 0.6.1 |
| created_at | 2025-07-18 15:49:10.429985+00 |
| updated_at | 2026-01-23 09:36:07.000974+00 |
| description | A functional programming library for Rust featuring your favourite higher-kinded types and type classes. |
| homepage | |
| repository | https://github.com/nothingnesses/rust-fp-library |
| max_upload_size | |
| id | 1759105 |
| size | 558,582 |
A functional programming library for Rust featuring your favourite higher-kinded types and type classes.
def_kind!, impl_kind!, Apply!) to simplify HKT boilerplate and type application.Functor, Applicative, MonadSemigroup, MonoidFoldable, TraversableCompactable, Filterable, WitherableCategory, SemigroupoidPointed, Lift, Defer, OnceApplyFirst, ApplySecond, Semiapplicative, SemimonadFunction, CloneableFn, SendCloneableFn, ParFoldable (Function wrappers and thread-safe operations)Pointer, RefCountedPointer, SendRefCountedPointer (Pointer abstraction)TrySemigroup, TryMonoid, SendDefercompose, constant, flip, identityOption, Result, Vec, StringIdentity, Lazy, PairEndofunction, Endomorphism, SendEndofunctionRcBrand, ArcBrand, FnBrandOnceCell, OnceLockRust 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 by providing:
Functor, Monad, Traversable, etc.).Add fp-library to your Cargo.toml:
[dependencies]
fp-library = "0.6"
The library offers optional features that can be enabled in your Cargo.toml:
rayon: Enables parallel folding operations (ParFoldable) and parallel execution support for VecBrand using the rayon library.To enable this feature:
[dependencies]
fp-library = { version = "0.6", features = ["rayon"] }
Functor with Optionuse fp_library::{brands::*, functions::*};
fn main() {
let x = Some(5);
// Map a function over the `Option` using the `Functor` type class
let y = map::<OptionBrand, _, _, _>(|i| i * 2, x);
assert_eq!(y, Some(10));
}
Since Rust doesn't support HKTs directly (e.g., trait Functor<F<_>>), this library uses Lightweight Higher-Kinded Polymorphism (also known as the "Brand" pattern or type-level defunctionalization).
Each type constructor has a corresponding Brand type (e.g., OptionBrand for Option). These brands implement the Kind traits, which map the brand and generic arguments back to the concrete type. The library provides macros to simplify this process.
use fp_library::{impl_kind, kinds::*};
pub struct OptionBrand;
impl_kind! {
for OptionBrand {
type Of<'a, A: 'a>: 'a = Option<A>;
}
}
Unlike many functional programming libraries that strictly adhere to curried functions (e.g., map(f)(fa)), fp-library adopts uncurried semantics (e.g., map(f, fa)) for its core abstractions.
Why? Traditional currying in Rust often requires:
Rc/Arc) to satisfy type system constraints.dyn Fn), which inhibits compiler optimizations like inlining.By using uncurried functions with impl Fn or generic bounds, fp-library achieves zero-cost abstractions:
map and bind do not allocate intermediate closures.This approach ensures that using high-level functional abstractions incurs no runtime penalty compared to hand-written imperative code.
Exceptions: While the library strives for zero-cost abstractions, some operations inherently require dynamic dispatch or heap allocation due to Rust's type system:
Vec for Semiapplicative::apply, or in Lazy thunks), they must often be "type-erased" (wrapped in Rc<dyn Fn> or Arc<dyn Fn>). This is because every closure in Rust has a unique, anonymous type. To store multiple different closures in the same container, or to compose functions dynamically (like in Endofunction), they must be coerced to a common trait object.Lazy type relies on storing a thunk that can be cloned and evaluated later, which typically requires reference counting and dynamic dispatch.For these specific cases, the library provides Brand types (like RcFnBrand and ArcFnBrand) to let you choose the appropriate wrapper (single-threaded vs. thread-safe) while keeping the rest of your code zero-cost. The library uses a unified Pointer hierarchy to abstract over these choices.
The library supports thread-safe operations through the SendCloneableFn extension trait and parallel folding via ParFoldable.
SendCloneableFn: Extends CloneableFn to provide Send + Sync function wrappers. Implemented by ArcFnBrand.ParFoldable: Provides par_fold_map and par_fold_right for parallel execution.VecBrand supports parallel execution using rayon when the rayon feature is enabled.use fp_library::{brands::*, functions::*};
let v = vec![1, 2, 3, 4, 5];
// Create a thread-safe function wrapper
let f = send_cloneable_fn_new::<ArcFnBrand, _, _>(|x: i32| x.to_string());
// Fold in parallel (if rayon feature is enabled)
let result = par_fold_map::<ArcFnBrand, VecBrand, _, _>(f, v);
assert_eq!(result, "12345".to_string());
We welcome contributions! Please feel free to submit a Pull Request.
This project uses Nix to manage the development environment.
To set up the environment:
# If using direnv
direnv allow
# Or manually enter the shell
nix develop
This will provide a shell with the correct Rust version and dependencies.
fp-library/src/classes: Contains the definitions of type classes (traits).fp-library/src/types: Contains implementations of type classes for various data types.fp-library/src/kinds: Contains the machinery for higher-kinded types.fp-library/src/brands: Contains type brands used for HKT encoding.fp-library/src/functions: Contains general helper functions.fp-macros: Procedural macros for generating HKT traits and implementations.For maintainers, the release process is documented in docs/release-process.md.
This project is licensed under the Blue Oak Model License 1.0.0.