Crate monadify

Source
Expand description

§monadify: Functional Programming Constructs in Rust

monadify is a Rust library that provides implementations of common functional programming constructs, with a primary focus on monads and related concepts like Functors, Applicatives, and Profunctors. The goal is to offer a practical exploration of these patterns in idiomatic Rust, serving as both a learning resource and a potentially reusable library component.

§Core Concepts Implemented

The library defines and implements the following core functional programming traits:

  • Functor: Types that can be mapped over. Provides map(self, f: A -> B) -> F<B>.
    • Implemented for Option<A>, Result<A, E>, Vec<A>, CFn<X, A>, CFnOnce<X, A>.
  • Apply: Extends Functor. Provides apply(self, f: F<A -> B>) -> F<B> for applying a wrapped function to a wrapped value.
    • Implemented for Option<A>, Result<A, E>, Vec<A>.
  • Applicative: Extends Apply. Provides pure(x: A) -> F<A> for lifting a value into the applicative context.
    • Implemented for Option<A>, Result<A, E>, Vec<A>.
  • Bind: Extends Apply. Provides bind(self, f: A -> F<B>) -> F<B> (also known as flatMap or >>=) for sequencing operations.
    • Implemented for Option<A>, Result<A, E>, Vec<A>.
  • Monad: A marker trait that groups Applicative and Bind.
    • Implemented for Option<A>, Result<A, E>, Vec<A>.
  • Profunctor: Bifunctors contravariant in the first argument and covariant in the second. Provides dimap(self, f: X -> A, g: B -> Y) -> P<X, Y>.
    • Implemented for CFn<A, B> and CFnOnce<A, B>.
  • Strong: Extends Profunctor. Provides first and second for operating on product types (tuples).
    • Implemented for CFn<A, B>.
  • Choice: Extends Profunctor. Provides left and right for operating on sum types (Result).
    • Implemented for CFn<A, B>.

The library also includes CFn and CFnOnce wrappers for heap-allocated closures, and various helper functions and macros (e.g., lift2, lift_a1, fn0!, fn1!, _1, _2, view) for working with these abstractions. Optical structures like Lens and Getter (using Profunctor encoding) are also explored.

§Project Goals

  • To explore and understand monads and other functional patterns from a practical Rust implementation perspective.
  • To create a reusable library of these structures in idiomatic Rust.
  • To serve as an educational resource for learning about functional programming concepts in Rust.

§Usage Example

Here’s a quick example of using the Functor trait with Option (HKT is now the default):

use monadify::{Functor, OptionHKTMarker}; // Import HKT Functor and marker

let some_value: Option<i32> = Some(10);
// For HKT, Functor<A,B> is on the marker OptionHKTMarker
let mapped_value = OptionHKTMarker::map(some_value, |x| x * 2);
assert_eq!(mapped_value, Some(20));

let no_value: Option<i32> = None;
let mapped_none = OptionHKTMarker::map(no_value, |x: i32| x * 2);
assert_eq!(mapped_none, None);

And an example using Bind (often called flat_map):

use monadify::{Bind, OptionHKTMarker}; // Import HKT Bind and marker

fn try_parse_and_double(s: &str) -> Option<i32> {
    s.parse::<i32>().ok().map(|n| n * 2)
}

let opt_str: Option<String> = Some("5".to_string());

// For HKT, Bind<A,B> is on the marker OptionHKTMarker
    // The closure takes String because OptionHKTMarker::Applied<String> is Option<String>
    let result = OptionHKTMarker::bind(
        opt_str,
        |st: String| try_parse_and_double(&st) // Our function A -> F<B>
    );
    assert_eq!(result, Some(10));

    let opt_invalid_str: Option<String> = Some("hello".to_string());
    let result_invalid = OptionHKTMarker::bind(
        opt_invalid_str,
        |st: String| try_parse_and_double(&st)
    );
    assert_eq!(result_invalid, None);

For more detailed examples, please refer to the documentation comments within the source code and the test files in the tests/ directory.

§Building the Project

To build the library:

cargo build

§Running Tests

The library includes a comprehensive test suite to verify the laws of Functor, Applicative, Monad, etc. To run the default HKT tests:

cargo test

This suite includes over 140 tests covering HKT implementations (for Option, Result, Vec, Identity, CFn, ReaderT) and Profunctor laws, all passing.

To run tests for the legacy (non-HKT) implementations, use the legacy feature flag:

cargo test --features legacy

This suite includes over 80 tests for the legacy versions, also all passing.

§Running Benchmarks

Performance benchmarks for core operations are available using criterion.rs. To run the benchmarks:

cargo bench

The benchmark results can be found in target/criterion/report/index.html. Key findings from initial benchmarks:

  • Functor::map and Bind::bind for Option and Result show negligible overhead compared to native methods.
  • Apply::apply (which involves Box::new for CFn) has a small, consistent overhead (around 2-4 ns).
  • Vec operations show more overhead due to by-value semantics and heap allocations for CFn in some cases.

§License

This project is licensed under the terms of the MIT License.

Re-exports§

pub use applicative::Applicative;
pub use apply::Apply;
pub use functor::Functor;
pub use monad::Bind;
pub use monad::Monad;
pub use profunctor::Choice;
pub use profunctor::Profunctor;
pub use profunctor::Strong;
pub use transformers::reader::MonadReader;
pub use function::CFn;
pub use function::CFnOnce;
pub use identity::Identity;
pub use transformers::reader::ReaderT;
pub use transformers::reader::Reader;
pub use kind_based::kind::HKT;
pub use kind_based::kind::HKT1;
pub use kind_based::kind::OptionHKTMarker;
pub use kind_based::kind::ResultHKTMarker;
pub use kind_based::kind::VecHKTMarker;
pub use kind_based::kind::CFnHKTMarker;
pub use kind_based::kind::CFnOnceHKTMarker;
pub use crate::identity::IdentityHKTMarker;
pub use crate::transformers::reader::ReaderTHKTMarker;

Modules§

applicative
Provides the HKT Applicative trait and its implementations.
apply
Provides the HKT Apply trait (an extension of Functor) and its implementations.
function
Defines CFn and CFnOnce for heap-allocated, callable function wrappers.
functor
Provides the HKT Functor trait and its implementations.
identity
Defines the Identity monad and its HKT marker.
kind_based
Core infrastructure for Higher-Kinded Types (HKTs), including HKT and HKT1 traits, and various HKT marker types (e.g., OptionHKTMarker).
monad
Provides the HKT Monad and Bind traits and their implementations.
profunctor
Implements Profunctor, Strong, and Choice traits, primarily for function types.
transformers
Contains monad transformers like ReaderT. Monad transformers.
utils
Utility functions and macros, including fn0!, fn1!, etc.

Macros§

fn0
Creates a CFn (boxed Fn) from a nullary (0-argument) closure.
fn1
Creates a CFn (boxed Fn) from a unary (1-argument) closure.
fn2
Creates a curried function of two arguments, wrapped in CFn.
fn3
Creates a curried function of three arguments, wrapped in nested CFns.