lifted 0.1.0

Higher-kinded types in Rust.
Documentation
//! The higher-kinded trait of a `Bifunctor`.

use super::*;
use super::types::*;

pub trait Bifunctor {
    fn bimap<A, B, C, D, F: Fn(A) -> B, G: Fn(C) -> D>(
        f: F,
        g: G,
        x: K2<Self, A, C>,
    ) -> K2<Self, B, D>;

    fn first<A, B, C, F: Fn(A) -> B>(f: F, x: K2<Self, A, C>) -> K2<Self, B, C> {
        Self::bimap(f, |i| i, x)
    }
    fn second<A, B, C, F: Fn(B) -> C>(f: F, x: K2<Self, A, B>) -> K2<Self, A, C> {
        Self::bimap(|i| i, f, x)
    }
}

impl Bifunctor for PairC {
    fn bimap<A, B, C, D, F: Fn(A) -> B, G: Fn(C) -> D>(
        f: F,
        g: G,
        x: K2<Self, A, C>,
    ) -> K2<Self, B, D> {
        let x = x.into_inner();
        PairC::new((f(x.0), g(x.1)))
    }
}

impl Bifunctor for ResultC {
    fn bimap<A, B, C, D, F: Fn(A) -> B, G: Fn(C) -> D>(
        f: F,
        g: G,
        x: K2<Self, A, C>,
    ) -> K2<Self, B, D> {
        Self::new(match x.into_inner() {
            Ok(a) => Ok(f(a)),
            Err(b) => Err(g(b)),
        })
    }
}

#[cfg(test)]
mod test {
    use super::*;

    fn check_law_id<T, U, B: Bifunctor + Kind2<T, U>>(input: K2<B, T, U>)
    where
        K2Type<B, T, U>: PartialEq + core::fmt::Debug + Clone,
    {
        let output = B::bimap(|x| x, |y| y, input.clone());
        assert_eq!(input, output);
    }

    fn check_law_compose<T, U, B: Bifunctor, V, W, X, Y, F, G, H, I>(
        f: F,
        g: G,
        h: H,
        i: I,
        input: K2<B, T, U>,
    ) where
        B: Kind2<T, U> + Kind2<X, Y>,
        F: Fn(V) -> X + Clone,
        G: Fn(T) -> V + Clone,
        H: Fn(W) -> Y + Clone,
        I: Fn(U) -> W + Clone,
        K2Type<B, T, U>: Clone,
        K2Type<B, X, Y>: PartialEq + core::fmt::Debug + Clone,
    {
        let composed_second = B::bimap(
            f.clone(),
            h.clone(),
            B::bimap(g.clone(), i.clone(), input.clone()),
        );
        let composed_first = B::bimap(|x| f(g(x)), |y| h(i(y)), input);
        assert_eq!(composed_first, composed_second);
    }

    #[test]
    fn pair_bifunctor_law_id() {
        check_law_id(PairC::new((42, false)));
    }

    #[test]
    fn pair_bifunctor_law_compose() {
        check_law_compose(
            |x| x + 1,
            |x| x * 2,
            |x| x - 1.0,
            |x| x * 3.0,
            PairC::new((42, 100.0)),
        );
    }

    #[test]
    fn result_bifunctor_law_id() {
        let just_fine: Result<i32, bool> = Ok(42);
        check_law_id(ResultC::new(just_fine));
        let not_fine: Result<i32, bool> = Err(true);
        check_law_id(ResultC::new(not_fine));
    }

    #[test]
    fn result_bifunctor_law_compose() {
        let just_fine: Result<i32, f32> = Ok(42);
        check_law_compose(
            |x| x + 1,
            |x| x * 2,
            |x| x - 1.0,
            |x| x * 3.0,
            ResultC::new(just_fine),
        );
        let not_fine: Result<i32, f32> = Err(100.0);
        check_law_compose(
            |x| x + 1,
            |x| x * 2,
            |x| x - 1.0,
            |x| x * 3.0,
            ResultC::new(not_fine),
        );
    }
}