lifted 0.1.0

Higher-kinded types in Rust.
Documentation
//! The higher-kinded trait of an `Applicative` functor.

use super::functor::Functor;
use super::*;
use super::types::*;

pub trait Applicative: Functor {
    fn pure<T>(t: T) -> K1<Self, T>;
    fn app<A: Clone, B, F: Fn(A) -> B>(f: K1<Self, F>, a: K1<Self, A>) -> K1<Self, B>;
}

impl Applicative for OptionC {
    fn pure<T>(t: T) -> K1<Self, T> {
        OptionC::new(Some(t))
    }

    fn app<A, B, F: Fn(A) -> B>(f: K1<Self, F>, a: K1<Self, A>) -> K1<Self, B> {
        OptionC::new(
            f.into_inner()
                .and_then(move |f| a.into_inner().map(move |a| f(a))),
        )
    }
}

impl Applicative for VecC {
    fn pure<T>(t: T) -> K1<Self, T> {
        VecC::new(vec![t])
    }

    fn app<A: Clone, B, F: Fn(A) -> B>(f: K1<Self, F>, a: K1<Self, A>) -> K1<Self, B> {
        VecC::new(
            f.into_inner()
                .into_iter()
                .flat_map(move |f| {
                    let f = &f;
                    a.inner()
                        .into_iter()
                        .map(move |a| f(a.clone()))
                        .collect::<Vec<_>>()
                })
                .collect()
        )
    }
}

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

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

    fn check_law_homomorphism<F: Applicative + Kind1<T>, T: Clone, G>(g: G, input: T)
    where
        G: Fn(T) -> T + Clone,
        K1Type<F, T>: PartialEq + core::fmt::Debug + Clone,
    {
        let pure_g = F::pure(g.clone());
        let pure_x = F::pure(input.clone());

        let pure_first = F::app(pure_g, pure_x);
        let app_first = F::pure(g(input));
        assert_eq!(pure_first, app_first);
    }

    fn check_law_interchange<F: Applicative + Kind1<T> + Kind1<G>, T: Clone, G>(
        u: K1<F, G>,
        input: T,
    ) where
        G: Fn(T) -> T + Clone,
        K1Type<F, G>: Clone,
        K1Type<F, T>: PartialEq + core::fmt::Debug + Clone,
    {
        fn call_with<A: Clone, B, F: Fn(A) -> B>(a: A) -> impl Fn(F) -> B {
            move |f| f(a.clone())
        }

        let pure_y = F::pure(input.clone());
        let call_me = F::pure(call_with(input));

        let u_first = F::app(u.clone(), pure_y);
        let y_first = F::app(call_me, u);
        assert_eq!(u_first, y_first);
    }

    fn check_law_composition<U, V, W, F: Applicative, X, Y>(u: K1<F, U>, v: K1<F, V>, w: K1<F, W>)
    where
        W: Clone,
        X: Clone,
        Y: Clone,
        F: Kind1<U> + Kind1<V> + Kind1<W> + Kind1<Y>,
        U: Fn(X) -> Y + Clone,
        K1Type<F, U>: Clone,
        V: Fn(W) -> X + Clone,
        K1Type<F, V>: Clone,
        K1Type<F, W>: Clone,
        K1Type<F, Y>: PartialEq + Clone + core::fmt::Debug,
    {
        let compose = |f: U| {
            move |g: V| {
                let f = f.clone();
                move |a| (f.clone())((g.clone())(a))
            }
        };

        let nested_right = F::app(u.clone(), F::app(v.clone(), w.clone()));
        let nested_left = F::app(F::app(F::app(F::pure(compose), u), v), w);

        assert_eq!(nested_right, nested_left);
    }

    #[test]
    fn option_applicative_law_id() {
        check_law_id::<i32, _>(OptionC::new(None));
        check_law_id(OptionC::new(Some(42)));
    }

    #[test]
    fn option_applicative_law_homomorphism() {
        let g = |x| x + 1;
        check_law_homomorphism::<OptionC, _, _>(&g, 42);
    }

    #[test]
    fn option_applicative_law_interchange() {
        let g = OptionC::pure(|x| x + 1);
        let no_g: K1<OptionC, fn(i32) -> i32> = OptionC::new(None);
        check_law_interchange(g, 42);
        check_law_interchange(no_g, 42);
    }

    #[test]
    fn option_applicative_law_composition() {
        check_law_composition(
            OptionC::new(Some(|x| x * 2)),
            OptionC::new(Some(|x| x + 1)),
            OptionC::new(Some(42)),
        );
        check_law_composition::<_, _, i32, _, _, _>(
            OptionC::new(Some(|x| x * 2)),
            OptionC::new(Some(|x| x + 1)),
            OptionC::new(None),
        );
        check_law_composition::<_, fn(i32) -> i32, _, _, _, _>(
            OptionC::new(Some(|x| x * 2)),
            OptionC::new(None),
            OptionC::new(Some(42)),
        );
        check_law_composition::<fn(i32) -> i32, _, _, _, _, _>(
            OptionC::new(None),
            OptionC::new(Some(|x| x + 1)),
            OptionC::new(Some(42)),
        );
    }

    #[test]
    fn vec_applicative_law_id() {
        check_law_id::<i32, _>(VecC::new(vec![]));
        check_law_id(VecC::new(vec![42]));
        check_law_id(VecC::new(vec![1, 2, 3]));
    }

    #[test]
    fn vec_applicative_law_homomorphism() {
        let g = |x| x + 1;
        check_law_homomorphism::<VecC, _, _>(&g, 42);
    }

    #[test]
    fn vec_applicative_law_interchange() {
        let g = VecC::pure(|x| x + 1);
        let no_g: K1<VecC, fn(i32) -> i32> = VecC::new(vec![]);
        check_law_interchange(g, 42);
        check_law_interchange(no_g, 42);
    }

    #[test]
    fn vec_applicative_law_composition() {
        check_law_composition(
            VecC::new(vec![|x| x * 2]),
            VecC::new(vec![|x| x + 1]),
            VecC::new(vec![42]),
        );
        check_law_composition::<_, _, i32, _, _, _>(
            VecC::new(vec![|x| x * 2]),
            VecC::new(vec![|x| x + 1]),
            VecC::new(vec![]),
        );
        check_law_composition::<_, fn(i32) -> i32, _, _, _, _>(
            VecC::new(vec![|x| x * 2]),
            VecC::new(vec![]),
            VecC::new(vec![42]),
        );
        check_law_composition::<fn(i32) -> i32, _, _, _, _, _>(
            VecC::new(vec![]),
            VecC::new(vec![|x| x + 1]),
            VecC::new(vec![42]),
        );
    }
}