Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 3 additions & 6 deletions docs/todo.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
### Tasks to do & ideas to look into

* Determine what to do about `.expect(` in the codebase. Are there any other sources of panics? Modify document_module to detect these?
* Optics:
* Full parity with `purescript-profunctor-lenses`.
* Implement `Prism`, `Iso`, `Traversal`, `Grate`, `Fold`, `Getter`, `Setter`, `Review`.
* `fp-macros`: `#[derive(Lens)]`, `#[derive(Prism)]`.
* Optics: Implement missing functionality from [analysis](optics-analysis.md).
* Algebraic effects/effect system to implement extensible effects
* [Eff](https://github.com/lexi-lambda/eff) [documentation](https://hasura.github.io/eff/Control-Effect.html)
* `Alternative` type class (requires `Plus` and `Applicative`).
* Add extra trait methods for type classes and methods for brand/type structs. Make trait implementations use type methods. E.g., add a map method for Identity, make the Functor implementation use it, like how Functor for Vec uses Iterator map internally.
* Kleisli composition (`compose_kleisli`, `>=>` equivalent). Composes monadic functions `A -> F<B>` and `B -> F<C>` into `A -> F<C>` without explicit `bind` threading. Enables point-free monadic pipelines and reusable monadic function building blocks.
* `do!` macro. Desugars sequential monadic binds from flat syntax into nested `bind` calls, e.g. `do! { x <- fa; y <- g(x); pure(x + y) }` becomes `bind(fa, |x| bind(g(x), |y| pure(x + y)))`. Eliminates rightward drift from deeply nested closures.
* Property-based tests for type class laws.
* [Validity](https://github.com/NorfairKing/validity).
* Add a diagram of the typeclass/trait hierarchy and reasoning/justification for why the current hierarchy is as it is.
Expand Down
15 changes: 15 additions & 0 deletions fp-library/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.11.1] - 2026-03-10

### Added
- **`Pipe` trait**: Left-to-right function application via method syntax (`value.pipe(f)`), similar to PureScript's `#` or Haskell's `&` operator.
- **`Bifoldable` implementations**: Added `Bifoldable` for `PairBrand` and `StepBrand`.
- **`Bitraversable` implementations**: Added `Bitraversable` for `PairBrand` and `StepBrand`.
- **Indexed type class implementations for `CatList`**: Added `FunctorWithIndex<usize>`, `FoldableWithIndex<usize>`, and `TraversableWithIndex<usize>` for `CatListBrand`.
- **Inherent methods**: Added inherent methods to data types, with type class trait implementations delegating to them:
- `Identity`: `map`, `lift2`, `apply`, `bind`, `fold_right`, `fold_left`, `fold_map`, `traverse`, `sequence`.
- `Pair`: `bimap`, `map_first`, `map_second`, `fold`, `bi_fold_right`, `bi_fold_left`, `bi_fold_map`, `bi_traverse`, `bind`, `bind_first`.
- `Step`: `bimap`, `map_loop`, `map_done`, `bi_fold_right`, `bi_fold_left`, `bi_fold_map`, `bi_traverse`, `fold_right`, `fold_left`, `fold_map`, `bind`, `bind_loop`.
- `ConstVal`: `map`, `lift2`, `apply_first`, `apply_second`, `pure`.
- `CatList`: `pure`, `map`, `bind`, `fold_right`, `fold_left`, `fold_map`, `map_with_index`, `fold_map_with_index`, `traverse_with_index`.
- `Lazy`: `ref_map`.

## [0.11.0] - 2026-03-10

### Changed
Expand Down
2 changes: 1 addition & 1 deletion fp-library/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "fp-library"
version = "0.11.0"
version = "0.11.1"
edition = "2024"
description = "A functional programming library for Rust featuring your favourite higher-kinded types and type classes."
readme = "../README.md"
Expand Down
1 change: 1 addition & 0 deletions fp-library/src/classes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub mod monad_rec;
pub mod monoid;
pub mod optics;
pub mod par_foldable;
pub mod pipe;
pub mod pointed;
pub mod pointer;
pub mod profunctor;
Expand Down
100 changes: 100 additions & 0 deletions fp-library/src/classes/pipe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//! Left-to-right function application via method syntax.
//!
//! Provides the [`Pipe`] trait for pipeline-style composition, similar to
//! PureScript's `#` operator or Haskell's `&` operator.
//!
//! ### Examples
//!
//! ```
//! use fp_library::{
//! brands::*,
//! classes::*,
//! functions::*,
//! };
//!
//! let result = Some(5).pipe(|x| map::<OptionBrand, _, _>(|n| n + 1, x));
//!
//! assert_eq!(result, Some(6));
//! ```

#[fp_macros::document_module]
mod inner {
use fp_macros::*;

/// A trait for left-to-right function application via method syntax.
///
/// `Pipe` provides the `.pipe()` method on all sized types via a blanket
/// implementation, enabling pipeline-style composition similar to
/// PureScript's `#` operator or Haskell's `&` operator.
///
/// This is particularly useful for composing operations on types where
/// inherent methods are not available (e.g., stdlib types like `Option`
/// and `Vec`).
#[document_parameters("The value to pipe.")]
pub trait Pipe: Sized {
/// Pipes `self` into a function, enabling left-to-right composition.
///
/// Applies `f` to `self` and returns the result. This is the method
/// syntax version of [`pipe`].
#[document_signature]
///
#[document_type_parameters("The return type of the function.")]
///
#[document_parameters("The function to apply to the value.")]
///
#[document_returns("The result of applying `f` to `self`.")]
#[document_examples]
///
/// ```
/// use fp_library::{
/// brands::*,
/// classes::*,
/// functions::*,
/// };
///
/// let result = Some(5)
/// .pipe(|x| map::<OptionBrand, _, _>(|n| n + 1, x))
/// .pipe(|x| bind::<OptionBrand, _, _>(x, |n| if n > 3 { Some(n) } else { None }));
///
/// assert_eq!(result, Some(6));
/// ```
fn pipe<B>(
self,
f: impl FnOnce(Self) -> B,
) -> B {
f(self)
}
}

#[document_type_parameters("The type that implements Pipe.")]
impl<T> Pipe for T {}

/// Pipes a value into a function, enabling left-to-right composition.
///
/// Free function version of [`Pipe::pipe`]. Applies `f` to `a` and
/// returns the result. This is equivalent to PureScript's `applyFlipped`
/// or Haskell's `(&)`.
#[document_signature]
///
#[document_type_parameters("The type of the input value.", "The return type of the function.")]
///
#[document_parameters("The value to pipe.", "The function to apply to the value.")]
///
#[document_returns("The result of applying `f` to `a`.")]
#[document_examples]
///
/// ```
/// use fp_library::functions::*;
///
/// let result = pipe(5, |x| x + 1);
/// assert_eq!(result, 6);
/// ```
pub fn pipe<A, B>(
a: A,
f: impl FnOnce(A) -> B,
) -> B {
f(a)
}
}

pub use inner::*;
Loading
Loading