lifted 0.1.0

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

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

pub trait Functor {
    fn fmap<A, B, F: Fn(A) -> B>(f: F, a: K1<Self, A>) -> K1<Self, B>;
}

impl Functor for OptionC {
    fn fmap<A, B, F: Fn(A) -> B>(f: F, a: K1<Self, A>) -> K1<Self, B> {
        K1::new(a.into_inner().map(f))
    }
}

impl Functor for VecC {
    fn fmap<A, B, F: Fn(A) -> B>(f: F, a: K1<Self, A>) -> K1<Self, B> {
        K1::new(a.into_inner().into_iter().map(f).collect())
    }
}

impl<T> Functor for ResultLeft<T> {
    fn fmap<A, B, F: Fn(A) -> B>(f: F, a: K1<Self, A>) -> K1<Self, B> {
        Self::new(a.into_inner().map_err(f))
    }
}

impl<E> Functor for ResultRight<E> {
    fn fmap<A, B, F: Fn(A) -> B>(f: F, a: K1<Self, A>) -> K1<Self, B> {
        Self::new(a.into_inner().map(f))
    }
}

impl<T> Functor for PairLeft<T> {
    fn fmap<A, B, F: Fn(A) -> B>(f: F, a: K1<Self, A>) -> K1<Self, B> {
        let a = a.into_inner();
        Self::new((a.0, f(a.1)))
    }
}

impl<T> Functor for PairRight<T> {
    fn fmap<A, B, F: Fn(A) -> B>(f: F, a: K1<Self, A>) -> K1<Self, B> {
        let a = a.into_inner();
        Self::new((f(a.0), a.1))
    }
}

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

    fn check_law_id<T, F: Functor + Kind1<T>>(input: K1<F, T>)
    where
        K1Type<F, T>: PartialEq + core::fmt::Debug + Clone,
    {
        let output = F::fmap(|x| x, input.clone());
        assert_eq!(input, output);
    }

    fn check_law_compose<T, F: Functor + Kind1<T>, G, H>(g: G, h: H, input: K1<F, T>)
    where
        G: Fn(T) -> T,
        H: Fn(T) -> T,
        K1Type<F, T>: PartialEq + core::fmt::Debug + Clone,
    {
        let composed_first = F::fmap(|x| g(h(x)), input.clone());
        let fmapped_first = F::fmap(g, F::fmap(h, input));
        assert_eq!(composed_first, fmapped_first);
    }

    #[test]
    fn option_functor_law_id() {
        let nothing: Option<i32> = None;
        check_law_id(OptionC::new(nothing));
        let something: Option<i32> = Some(42);
        check_law_id(OptionC::new(something));
    }

    #[test]
    fn option_functor_law_compose() {
        let g = |x| x + 1;
        let h = |x| x * 2;
        check_law_compose::<i32, _, _, _>(&g, &h, OptionC::new(None));
        check_law_compose(&g, &h, OptionC::new(Some(42)));
    }

    #[test]
    fn vec_functor_law_id() {
        let nothing: Vec<i32> = vec![];
        check_law_id(VecC::new(nothing));
        let something: Vec<i32> = vec![1, 2, 3];
        check_law_id(VecC::new(something));
    }

    #[test]
    fn vec_functor_law_compose() {
        let g = |x| x + 1;
        let h = |x| x * 2;
        check_law_compose::<i32, _, _, _>(&g, &h, VecC::new(vec![]));
        check_law_compose(&g, &h, VecC::new(vec![1, 2, 3]));
    }

    #[test]
    fn result_left_functor_law_id() {
        let great: Result<i32, String> = Ok(42);
        check_law_id(ResultLeft::new(great));
        let not_great: Result<String, i32> = Err(42);
        check_law_id(ResultLeft::new(not_great));
    }

    #[test]
    fn result_left_functor_law_compose() {
        let g = |x| x + 1;
        let h = |x| x * 2;
        check_law_compose::<i32, _, _, _>(&g, &h, ResultLeft::<i32>::new(Err(42)));
        check_law_compose(&g, &h, ResultLeft::new(Ok(42)));
    }

    #[test]
    fn result_right_functor_law_id() {
        let great: Result<i32, String> = Ok(42);
        check_law_id(ResultRight::new(great));
        let not_great: Result<String, i32> = Err(42);
        check_law_id(ResultRight::new(not_great));
    }

    #[test]
    fn result_right_functor_law_compose() {
        let g = |x| x + 1;
        let h = |x| x * 2;
        check_law_compose::<i32, _, _, _>(&g, &h, ResultRight::new(Err(42)));
        check_law_compose(&g, &h, ResultRight::<i32>::new(Ok(42)));
    }

    #[test]
    fn pair_left_functor_law_id() {
        check_law_id(PairLeft::new(("foobar", 42)));
    }

    #[test]
    fn pair_left_functor_law_compose() {
        let g = |x| x + 1;
        let h = |x| x * 2;
        check_law_compose(&g, &h, PairLeft::new(("foobar", 42)));
    }

    #[test]
    fn pair_right_functor_law_id() {
        check_law_id(PairRight::new((42, "foobar")));
    }

    #[test]
    fn pair_right_functor_law_compose() {
        let g = |x| x + 1;
        let h = |x| x * 2;
        check_law_compose(&g, &h, PairRight::new((42, "foobar")));
    }
}