Foldable & Traversable

Summarize and sequence container contents.

Foldable

Foldable

A structure that can be folded to a summary value.

Signature

pub trait Foldable: HKT {
    fn fold_right<A, B>(fa: Self::Of<A>, init: B, f: impl Fn(A, B) -> B) -> B;

    fn fold_map<A, M: Monoid>(fa: Self::Of<A>, f: impl Fn(A) -> M) -> M {
        Self::fold_right(fa, M::empty(), |a, acc| f(a).combine(acc))
    }
}

fold_right is the required method. It processes elements right-to-left, threading an accumulator through each step. fold_map is provided as a default: it maps each element into a Monoid and combines them.

Laws

fold_map consistency

fold_map(fa, f) == fold_right(fa, M::empty(), |a, acc| f(a).combine(acc))

Any override of the default fold_map must agree with the right fold formulation above.

Instances

Type constructorNotes
OptionFFolds over the contained value, if any; returns init for None.
ResultF<E>Folds over the Ok value; returns init for Err.
VecFRight-folds by reversing and iterating. Requires alloc.
IdentityFTrivially applies f to the single contained value.
NonEmptyVecFRight-folds the tail, then applies f to the head. Requires alloc.

Example: summing with fold_map

use karpal_std::prelude::*;

// fold_map maps each element into a Monoid and combines them.
// For i32, the Monoid instance uses addition with identity 0.

let sum = VecF::fold_map(vec![1, 2, 3], |a: i32| a);
assert_eq!(sum, 6); // 1 + 2 + 3

let sum = OptionF::fold_map(Some(42), |a: i32| a);
assert_eq!(sum, 42);

let sum = OptionF::fold_map(None::<i32>, |a: i32| a);
assert_eq!(sum, 0); // Monoid::empty() for i32

Example: fold_right

use karpal_std::prelude::*;

// fold_right processes elements right-to-left.
// With subtraction, the associativity matters:
// fold_right([1, 2, 3], 0, |a, b| a - b)
//   = 1 - (2 - (3 - 0))
//   = 1 - (2 - 3)
//   = 1 - (-1)
//   = 2
let result = VecF::fold_right(vec![1, 2, 3], 0, |a, b| a - b);
assert_eq!(result, 2);

Traversable

Traversable

A Functor + Foldable that can be traversed with an effectful function.

Signature

pub trait Traversable: Functor + Foldable {
    fn traverse<G, A, B, F>(fa: Self::Of<A>, f: F) -> G::Of<Self::Of<B>>
    where
        G: Applicative,
        F: Fn(A) -> G::Of<B>,
        B: Clone;
}

traverse applies an effectful function f to every element in the structure, collecting the results inside the effect G. If any application of f produces a "failure" (e.g. None for OptionF), the entire traversal short-circuits.

Laws

Identity

traverse::<IdentityF, _, _, _>(fa, pure) == pure(fa)

Composition

traverse::<Compose<F, G>, _, _, _>(fa, |a| Compose(F::fmap(f(a), g)))
== Compose(F::fmap(traverse::<F, _, _, _>(fa, f), |fb| traverse::<G, _, _, _>(fb, g)))

Naturality

t(traverse::<F, _, _, _>(fa, f)) == traverse::<G, _, _, _>(fa, |a| t(f(a)))
for any applicative natural transformation t: F ~> G

Karpal verifies the Identity law with property-based tests using OptionF as the effect.

Instances

Type constructorNotes
OptionFTraverses the inner value if Some; returns G::pure(None) for None.
ResultF<E>Traverses the Ok value; returns G::pure(Err(e)) for Err. Requires E: Clone.
VecFTraverses each element left-to-right, accumulating via Applicative::ap. Requires alloc and B: Clone.

Example: traverse with Option

use karpal_std::prelude::*;

// Parse a list of strings into integers, failing if any parse fails.
fn parse(s: &str) -> Option<i32> {
    s.parse().ok()
}

// All elements parse successfully:
let result = VecF::traverse::<OptionF, _, _, _>(
    vec!["1", "2", "3"],
    parse,
);
assert_eq!(result, Some(vec![1, 2, 3]));

// One element fails, so the whole traversal returns None:
let result = VecF::traverse::<OptionF, _, _, _>(
    vec!["1", "oops", "3"],
    parse,
);
assert_eq!(result, None);

Example: traverse over Option

use karpal_std::prelude::*;

// Traverse an Option with an effectful function:
let result = OptionF::traverse::<OptionF, _, _, _>(
    Some(3),
    |x| Some(x * 2),
);
assert_eq!(result, Some(Some(6)));

// If the inner effect fails:
let result = OptionF::traverse::<OptionF, _, _, _>(
    Some(3),
    |_x| None::<i32>,
);
assert_eq!(result, None);

// Traversing None always succeeds:
let result = OptionF::traverse::<OptionF, i32, i32, _>(
    None,
    |x| Some(x * 2),
);
assert_eq!(result, Some(None));