generic tests + CopyFunctor
This commit is contained in:
		
							parent
							
								
									0b0914bd4e
								
							
						
					
					
						commit
						85df7b2092
					
				
							
								
								
									
										141
									
								
								src/copy_func.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								src/copy_func.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,141 @@
 | 
			
		||||
use super::func::*;
 | 
			
		||||
 | 
			
		||||
pub trait CopyWeakFunctor {
 | 
			
		||||
    type CF<A: Copy>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait CopyFunctor: CopyWeakFunctor {
 | 
			
		||||
    fn copy_fmap<A: Copy, B: Copy, F: Fn(A) -> B>(f: F, fa: Self::CF<A>) -> Self::CF<B>;
 | 
			
		||||
 | 
			
		||||
    fn copy_replace<A: Copy, B: Copy>(fa: Self::CF<A>, b: B) -> Self::CF<B> {
 | 
			
		||||
        Self::copy_fmap(|_| b, fa)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn copy_void<A: Copy>(fa: Self::CF<A>) -> Self::CF<()> {
 | 
			
		||||
        Self::copy_replace(fa, ())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: WeakFunctor> CopyWeakFunctor for T {
 | 
			
		||||
    type CF<A: Copy> = T::F<A>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Functor> CopyFunctor for T {
 | 
			
		||||
    fn copy_fmap<A: Copy, B: Copy, F: Fn(A) -> B>(f: F, fa: Self::CF<A>) -> Self::CF<B> {
 | 
			
		||||
        Self::fmap(f, fa)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn copy_replace<A: Copy, B: Copy>(fa: Self::CF<A>, b: B) -> Self::CF<B> {
 | 
			
		||||
        Self::replace(fa, b)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn copy_void<A: Copy>(fa: Self::CF<A>) -> Self::CF<()> {
 | 
			
		||||
        Self::void(fa)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait CopyApplicativeSeq: CopyFunctor {
 | 
			
		||||
    fn copy_seq<A: Copy, B: Copy, F: Copy + Fn(A) -> B>(
 | 
			
		||||
        ff: Self::CF<F>,
 | 
			
		||||
        fa: Self::CF<A>,
 | 
			
		||||
    ) -> Self::CF<B>;
 | 
			
		||||
 | 
			
		||||
    fn _copy_la2<A: Copy, B: Copy, C: Copy, F: Copy + Fn(A, B) -> C>(
 | 
			
		||||
        f: F,
 | 
			
		||||
        fa: Self::CF<A>,
 | 
			
		||||
        fb: Self::CF<B>,
 | 
			
		||||
    ) -> Self::CF<C> {
 | 
			
		||||
        Self::copy_seq(Self::copy_fmap(|a| move |b| f(a, b), fa), fb)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: ApplicativeSeq> CopyApplicativeSeq for T {
 | 
			
		||||
    fn copy_seq<A: Copy, B: Copy, F: Copy + Fn(A) -> B>(
 | 
			
		||||
        ff: Self::CF<F>,
 | 
			
		||||
        fa: Self::CF<A>,
 | 
			
		||||
    ) -> Self::CF<B> {
 | 
			
		||||
        Self::seq(ff, fa)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait CopyApplicativeLA2: CopyFunctor {
 | 
			
		||||
    fn copy_la2<A: Copy, B: Copy, C: Copy, F: Fn(A, B) -> C>(
 | 
			
		||||
        f: F,
 | 
			
		||||
        fa: Self::CF<A>,
 | 
			
		||||
        fb: Self::CF<B>,
 | 
			
		||||
    ) -> Self::CF<C>;
 | 
			
		||||
 | 
			
		||||
    fn _copy_seq<A: Copy, B: Copy, F: Copy + Fn(A) -> B>(
 | 
			
		||||
        ff: Self::CF<F>,
 | 
			
		||||
        fa: Self::CF<A>,
 | 
			
		||||
    ) -> Self::CF<B> {
 | 
			
		||||
        Self::copy_la2(|f, a| f(a), ff, fa)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: ApplicativeLA2> CopyApplicativeLA2 for T {
 | 
			
		||||
    fn copy_la2<A: Copy, B: Copy, C: Copy, F: Fn(A, B) -> C>(
 | 
			
		||||
        f: F,
 | 
			
		||||
        fa: Self::CF<A>,
 | 
			
		||||
        fb: Self::CF<B>,
 | 
			
		||||
    ) -> Self::CF<C> {
 | 
			
		||||
        Self::la2(f, fa, fb)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait CopyApplicative: CopyFunctor + CopyApplicativeSeq + CopyApplicativeLA2 {
 | 
			
		||||
    fn copy_pure<A: Copy>(a: A) -> Self::CF<A>;
 | 
			
		||||
 | 
			
		||||
    fn copy_discard_first<A: Copy, B: Copy>(fa: Self::CF<A>, fb: Self::CF<B>) -> Self::CF<B> {
 | 
			
		||||
        Self::copy_seq(Self::copy_replace(fa, |b| b), fb)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn copy_discard_second<A: Copy, B: Copy>(fa: Self::CF<A>, fb: Self::CF<B>) -> Self::CF<A> {
 | 
			
		||||
        Self::copy_la2(|a, _| a, fa, fb)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Applicative> CopyApplicative for T {
 | 
			
		||||
    fn copy_pure<A: Copy>(a: A) -> Self::CF<A> {
 | 
			
		||||
        Self::pure(a)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn copy_discard_first<A: Copy, B: Copy>(fa: Self::CF<A>, fb: Self::CF<B>) -> Self::CF<B> {
 | 
			
		||||
        Self::discard_first(fa, fb)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn copy_discard_second<A: Copy, B: Copy>(fa: Self::CF<A>, fb: Self::CF<B>) -> Self::CF<A> {
 | 
			
		||||
        Self::discard_second(fa, fb)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait CopyMonad: CopyApplicative {
 | 
			
		||||
    fn copy_bind<A: Copy, B: Copy, F: FnOnce(A) -> Self::CF<B>>(
 | 
			
		||||
        fa: Self::CF<A>,
 | 
			
		||||
        f: F,
 | 
			
		||||
    ) -> Self::CF<B>;
 | 
			
		||||
 | 
			
		||||
    fn copy_join<A: Copy>(ffa: Self::CF<Self::CF<A>>) -> Self::CF<A>
 | 
			
		||||
    where
 | 
			
		||||
        Self::CF<A>: Copy,
 | 
			
		||||
    {
 | 
			
		||||
        // ugly
 | 
			
		||||
        Self::copy_bind(ffa, |fa| fa)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Monad> CopyMonad for T {
 | 
			
		||||
    fn copy_bind<A: Copy, B: Copy, F: FnOnce(A) -> Self::CF<B>>(
 | 
			
		||||
        fa: Self::CF<A>,
 | 
			
		||||
        f: F,
 | 
			
		||||
    ) -> Self::CF<B> {
 | 
			
		||||
        T::bind(fa, f)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn copy_join<A: Copy>(ffa: Self::CF<Self::CF<A>>) -> Self::CF<A>
 | 
			
		||||
    where
 | 
			
		||||
        Self::CF<A>: Copy,
 | 
			
		||||
    {
 | 
			
		||||
        T::join(ffa)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										542
									
								
								src/func.rs
									
									
									
									
									
								
							
							
						
						
									
										542
									
								
								src/func.rs
									
									
									
									
									
								
							@ -1,6 +1,8 @@
 | 
			
		||||
pub trait Functor {
 | 
			
		||||
    type F<T>;
 | 
			
		||||
pub trait WeakFunctor {
 | 
			
		||||
    type F<A>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait Functor: WeakFunctor {
 | 
			
		||||
    fn fmap<A, B, F: FnOnce(A) -> B>(f: F, fa: Self::F<A>) -> Self::F<B>;
 | 
			
		||||
 | 
			
		||||
    fn replace<A, B>(fa: Self::F<A>, b: B) -> Self::F<B> {
 | 
			
		||||
@ -14,28 +16,18 @@ pub trait Functor {
 | 
			
		||||
 | 
			
		||||
pub trait ApplicativeSeq: Functor {
 | 
			
		||||
    fn seq<A, B, F: FnOnce(A) -> B>(ff: Self::F<F>, fa: Self::F<A>) -> Self::F<B>;
 | 
			
		||||
    fn _la2<A, B, C, F: FnOnce(A, B) -> C>(f: F, fa: Self::F<A>, fb: Self::F<B>) -> Self::F<C> {
 | 
			
		||||
        Self::seq(Self::fmap(|a| |b| f(a, b), fa), fb)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait ApplicativeLA2: Functor {
 | 
			
		||||
    fn la2<A, B, C, F: FnOnce(A, B) -> C>(f: F, fa: Self::F<A>, fb: Self::F<B>) -> Self::F<C>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait ApplicativeSeqOnly: ApplicativeSeq {}
 | 
			
		||||
 | 
			
		||||
pub trait ApplicativeLA2Only: ApplicativeLA2 {}
 | 
			
		||||
 | 
			
		||||
impl<T: ApplicativeLA2Only> ApplicativeSeq for T {
 | 
			
		||||
    fn seq<A, B, F: FnOnce(A) -> B>(ff: Self::F<F>, fa: Self::F<A>) -> Self::F<B> {
 | 
			
		||||
    fn _seq<A, B, F: FnOnce(A) -> B>(ff: Self::F<F>, fa: Self::F<A>) -> Self::F<B> {
 | 
			
		||||
        Self::la2(|f, a| f(a), ff, fa)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: ApplicativeSeqOnly> ApplicativeLA2 for T {
 | 
			
		||||
    fn la2<A, B, C, F: FnOnce(A, B) -> C>(f: F, fa: Self::F<A>, fb: Self::F<B>) -> Self::F<C> {
 | 
			
		||||
        Self::seq(Self::fmap(|a| |b| f(a, b), fa), fb)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait Applicative: Functor + ApplicativeSeq + ApplicativeLA2 {
 | 
			
		||||
    fn pure<A>(a: A) -> Self::F<A>;
 | 
			
		||||
 | 
			
		||||
@ -56,80 +48,75 @@ pub trait Monad: Applicative {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct OptionClass;
 | 
			
		||||
 | 
			
		||||
impl Functor for OptionClass {
 | 
			
		||||
    type F<T> = Option<T>;
 | 
			
		||||
 | 
			
		||||
    fn fmap<A, B, F: FnOnce(A) -> B>(f: F, fa: Self::F<A>) -> Self::F<B> {
 | 
			
		||||
        match fa {
 | 
			
		||||
            Some(a) => Some(f(a)),
 | 
			
		||||
            None => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn replace<A, B>(fa: Self::F<A>, b: B) -> Self::F<B> {
 | 
			
		||||
        match fa {
 | 
			
		||||
            Some(_) => Some(b),
 | 
			
		||||
            None => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ApplicativeSeq for OptionClass {
 | 
			
		||||
    fn seq<A, B, F: FnOnce(A) -> B>(ff: Self::F<F>, fa: Self::F<A>) -> Self::F<B> {
 | 
			
		||||
        match (ff, fa) {
 | 
			
		||||
            (Some(f), Some(a)) => Some(f(a)),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ApplicativeLA2 for OptionClass {
 | 
			
		||||
    fn la2<A, B, C, F: FnOnce(A, B) -> C>(f: F, fa: Self::F<A>, fb: Self::F<B>) -> Self::F<C> {
 | 
			
		||||
        match (fa, fb) {
 | 
			
		||||
            (Some(a), Some(b)) => Some(f(a, b)),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Applicative for OptionClass {
 | 
			
		||||
    fn pure<A>(a: A) -> Self::F<A> {
 | 
			
		||||
        Some(a)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn discard_first<A, B>(fa: Self::F<A>, fb: Self::F<B>) -> Self::F<B> {
 | 
			
		||||
        match fa {
 | 
			
		||||
            Some(_) => fb,
 | 
			
		||||
            None => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Monad for OptionClass {
 | 
			
		||||
    fn bind<A, B, F: FnOnce(A) -> Self::F<B>>(fa: Self::F<A>, f: F) -> Self::F<B> {
 | 
			
		||||
        match fa {
 | 
			
		||||
            Some(a) => f(a),
 | 
			
		||||
            None => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
pub mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use core::fmt::Debug;
 | 
			
		||||
    use std::ops::{Add, AddAssign};
 | 
			
		||||
 | 
			
		||||
    pub fn make_none<T>(_: Option<T>) -> Option<T> {
 | 
			
		||||
        None
 | 
			
		||||
    pub struct TestResults(usize, usize, Result<(), String>);
 | 
			
		||||
 | 
			
		||||
    pub type R = TestResults;
 | 
			
		||||
 | 
			
		||||
    impl R {
 | 
			
		||||
        pub fn unwrap(self) {
 | 
			
		||||
            match self {
 | 
			
		||||
                TestResults(success, total, Err(e)) => panic!("failed:\n{success}/{total}\n{e}\n"),
 | 
			
		||||
                _ => (),
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn fmap_respects_identity<T: Functor, A, FA0: Fn() -> T::F<A>>(fa0: FA0)
 | 
			
		||||
    fn eqr<T: PartialEq + Debug>(name: &str, left: T, right: T) -> R {
 | 
			
		||||
        if left == right {
 | 
			
		||||
            TestResults(1, 1, Ok(()))
 | 
			
		||||
        } else {
 | 
			
		||||
            TestResults(0, 1, Err(format!("{name}: {:?} != {:?}", left, right)))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl Default for R {
 | 
			
		||||
        fn default() -> Self {
 | 
			
		||||
            Self(0, 0, Ok(()))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl Add for R {
 | 
			
		||||
        type Output = R;
 | 
			
		||||
 | 
			
		||||
        fn add(self, rhs: Self) -> Self::Output {
 | 
			
		||||
            Self(
 | 
			
		||||
                self.0 + rhs.0,
 | 
			
		||||
                self.1 + rhs.1,
 | 
			
		||||
                match (self.2, rhs.2) {
 | 
			
		||||
                    (Err(le), Err(re)) => Err(le + "\n" + re.as_str()),
 | 
			
		||||
                    (e, Ok(_)) => e,
 | 
			
		||||
                    (Ok(_), e) => e,
 | 
			
		||||
                },
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl AddAssign<R> for R {
 | 
			
		||||
        fn add_assign(&mut self, rhs: R) {
 | 
			
		||||
            self.0 += rhs.0;
 | 
			
		||||
            self.1 += rhs.1;
 | 
			
		||||
            match (&mut self.2, rhs.2) {
 | 
			
		||||
                (Err(ref mut le), Err(re)) => {
 | 
			
		||||
                    *le += "\n";
 | 
			
		||||
                    *le += re.as_str()
 | 
			
		||||
                }
 | 
			
		||||
                (_, Ok(_)) => {}
 | 
			
		||||
                (lr, Err(re)) => *lr = Err(re),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn fmap_respects_identity<T: Functor, A, FA0: Fn() -> T::F<A>>(fa0: FA0) -> R
 | 
			
		||||
    where
 | 
			
		||||
        T::F<A>: Debug + PartialEq,
 | 
			
		||||
    {
 | 
			
		||||
        assert_eq!(T::fmap(|a| a, fa0()), fa0());
 | 
			
		||||
        eqr("identity: fmap id == id", T::fmap(|a| a, fa0()), fa0())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn fmap_respects_composition<
 | 
			
		||||
@ -144,17 +131,26 @@ mod tests {
 | 
			
		||||
        f: F,
 | 
			
		||||
        g: G,
 | 
			
		||||
        fa0: FA0,
 | 
			
		||||
    ) where
 | 
			
		||||
    ) -> R
 | 
			
		||||
    where
 | 
			
		||||
        T::F<C>: Debug + PartialEq,
 | 
			
		||||
    {
 | 
			
		||||
        assert_eq!(T::fmap(|a| f(g(a)), fa0()), T::fmap(f, T::fmap(g, fa0())));
 | 
			
		||||
        eqr(
 | 
			
		||||
            "composition: fmap (f . g) == fmap f . fmap g",
 | 
			
		||||
            T::fmap(|a| f(g(a)), fa0()),
 | 
			
		||||
            T::fmap(f, T::fmap(g, fa0())),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn seq_respects_identity<T: Applicative, A, FA0: Fn() -> T::F<A>>(fa0: FA0)
 | 
			
		||||
    pub fn seq_respects_identity<T: Applicative, A, FA0: Fn() -> T::F<A>>(fa0: FA0) -> R
 | 
			
		||||
    where
 | 
			
		||||
        T::F<A>: Debug + PartialEq,
 | 
			
		||||
    {
 | 
			
		||||
        assert_eq!(T::seq(T::pure(|a| a), fa0()), fa0());
 | 
			
		||||
        eqr(
 | 
			
		||||
            "identity: pure id <*> v = v",
 | 
			
		||||
            T::seq(T::pure(|a| a), fa0()),
 | 
			
		||||
            fa0(),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn seq_respects_composition<
 | 
			
		||||
@ -171,28 +167,35 @@ mod tests {
 | 
			
		||||
        ff0: FF0,
 | 
			
		||||
        fg0: FG0,
 | 
			
		||||
        fa0: FA0,
 | 
			
		||||
    ) where
 | 
			
		||||
    ) -> R
 | 
			
		||||
    where
 | 
			
		||||
        T::F<C>: Debug + PartialEq,
 | 
			
		||||
    {
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
        eqr(
 | 
			
		||||
            "composition: pure (.) <*> u <*> v <*> w = u <*> (v <*> w)",
 | 
			
		||||
            T::seq(
 | 
			
		||||
                T::seq(
 | 
			
		||||
                    T::seq(T::pure(|f: F| |g: G| move |a| f(g(a))), ff0()),
 | 
			
		||||
                    fg0()
 | 
			
		||||
                    fg0(),
 | 
			
		||||
                ),
 | 
			
		||||
                fa0()
 | 
			
		||||
                fa0(),
 | 
			
		||||
            ),
 | 
			
		||||
            T::seq(ff0(), T::seq(fg0(), fa0()))
 | 
			
		||||
        );
 | 
			
		||||
            T::seq(ff0(), T::seq(fg0(), fa0())),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn seq_is_homomorphic<T: Applicative, A, B, A0: Fn() -> A, F: Copy + Fn(A) -> B>(
 | 
			
		||||
        f: F,
 | 
			
		||||
        a0: A0,
 | 
			
		||||
    ) where
 | 
			
		||||
    ) -> R
 | 
			
		||||
    where
 | 
			
		||||
        T::F<B>: Debug + PartialEq,
 | 
			
		||||
    {
 | 
			
		||||
        assert_eq!(T::seq(T::pure(f), T::pure(a0())), T::pure(f(a0())));
 | 
			
		||||
        eqr(
 | 
			
		||||
            "homomorphism: pure f <*> pure x = pure (f x)",
 | 
			
		||||
            T::seq(T::pure(f), T::pure(a0())),
 | 
			
		||||
            T::pure(f(a0())),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn seq_respects_interchange<
 | 
			
		||||
@ -205,13 +208,15 @@ mod tests {
 | 
			
		||||
    >(
 | 
			
		||||
        ff0: FF0,
 | 
			
		||||
        a0: A0,
 | 
			
		||||
    ) where
 | 
			
		||||
    ) -> R
 | 
			
		||||
    where
 | 
			
		||||
        T::F<B>: Debug + PartialEq,
 | 
			
		||||
    {
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
        eqr(
 | 
			
		||||
            "interchange: u <*> pure y = pure ($ y) <*> u",
 | 
			
		||||
            T::seq(ff0(), T::pure(a0())),
 | 
			
		||||
            T::seq(T::pure(|f: F| f(a0())), ff0())
 | 
			
		||||
        );
 | 
			
		||||
            T::seq(T::pure(|f: F| f(a0())), ff0()),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn seq_can_be_expressed_via_la2<
 | 
			
		||||
@ -224,10 +229,15 @@ mod tests {
 | 
			
		||||
    >(
 | 
			
		||||
        ff0: FF0,
 | 
			
		||||
        fa0: FA0,
 | 
			
		||||
    ) where
 | 
			
		||||
    ) -> R
 | 
			
		||||
    where
 | 
			
		||||
        T::F<B>: Debug + PartialEq,
 | 
			
		||||
    {
 | 
			
		||||
        assert_eq!(T::seq(ff0(), fa0()), T::la2(|f, a| f(a), ff0(), fa0()));
 | 
			
		||||
        eqr(
 | 
			
		||||
            "seq via la2: (<*>) = liftA2 id",
 | 
			
		||||
            T::seq(ff0(), fa0()),
 | 
			
		||||
            T::la2(|f, a| f(a), ff0(), fa0()),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn fmap_can_be_expressed_via_seq<
 | 
			
		||||
@ -239,10 +249,15 @@ mod tests {
 | 
			
		||||
    >(
 | 
			
		||||
        f: F,
 | 
			
		||||
        fa0: FA0,
 | 
			
		||||
    ) where
 | 
			
		||||
    ) -> R
 | 
			
		||||
    where
 | 
			
		||||
        T::F<B>: Debug + PartialEq,
 | 
			
		||||
    {
 | 
			
		||||
        assert_eq!(T::fmap(f, fa0()), T::seq(T::pure(f), fa0()));
 | 
			
		||||
        eqr(
 | 
			
		||||
            "fmap via seq: fmap f x = pure f <*> x",
 | 
			
		||||
            T::fmap(f, fa0()),
 | 
			
		||||
            T::seq(T::pure(f), fa0()),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn discard_can_be_expressed_via_seq_or_la2<
 | 
			
		||||
@ -254,33 +269,44 @@ mod tests {
 | 
			
		||||
    >(
 | 
			
		||||
        fa0: FA0,
 | 
			
		||||
        fb0: FB0,
 | 
			
		||||
    ) where
 | 
			
		||||
    ) -> R
 | 
			
		||||
    where
 | 
			
		||||
        T::F<B>: Debug + PartialEq,
 | 
			
		||||
    {
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
        eqr(
 | 
			
		||||
            "discard via seq: u *> v = (id <$ u) <*> v",
 | 
			
		||||
            T::discard_first(fa0(), fb0()),
 | 
			
		||||
            T::seq(T::replace(fa0(), |b| b), fb0())
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            T::seq(T::replace(fa0(), |b| b), fb0()),
 | 
			
		||||
        ) + eqr(
 | 
			
		||||
            "discard via la2: u <* v = liftA2 const u v",
 | 
			
		||||
            T::discard_second(fb0(), fa0()),
 | 
			
		||||
            T::la2(|b, _| b, fb0(), fa0())
 | 
			
		||||
        );
 | 
			
		||||
            T::la2(|b, _| b, fb0(), fa0()),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn bind_respects_left_identity<T: Monad, A, B, F: Copy + Fn(A) -> T::F<B>, A0: Fn() -> A>(
 | 
			
		||||
        f: F,
 | 
			
		||||
        a0: A0,
 | 
			
		||||
    ) where
 | 
			
		||||
    ) -> R
 | 
			
		||||
    where
 | 
			
		||||
        T::F<B>: Debug + PartialEq,
 | 
			
		||||
    {
 | 
			
		||||
        assert_eq!(T::bind(T::pure(a0()), f), f(a0()));
 | 
			
		||||
        eqr(
 | 
			
		||||
            "left identity: pure a >>= k = k a",
 | 
			
		||||
            T::bind(T::pure(a0()), f),
 | 
			
		||||
            f(a0()),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn bind_respects_right_identity<T: Monad, A, FA0: Fn() -> T::F<A>>(fa0: FA0)
 | 
			
		||||
    pub fn bind_respects_right_identity<T: Monad, A, FA0: Fn() -> T::F<A>>(fa0: FA0) -> R
 | 
			
		||||
    where
 | 
			
		||||
        T::F<A>: Debug + PartialEq,
 | 
			
		||||
    {
 | 
			
		||||
        assert_eq!(T::bind(fa0(), T::pure), fa0());
 | 
			
		||||
        eqr(
 | 
			
		||||
            "right identity: m >>= bind = m",
 | 
			
		||||
            T::bind(fa0(), T::pure),
 | 
			
		||||
            fa0(),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn bind_is_associative<
 | 
			
		||||
@ -295,13 +321,15 @@ mod tests {
 | 
			
		||||
        f: F,
 | 
			
		||||
        g: G,
 | 
			
		||||
        fa0: FA0,
 | 
			
		||||
    ) where
 | 
			
		||||
    ) -> R
 | 
			
		||||
    where
 | 
			
		||||
        T::F<C>: Debug + PartialEq,
 | 
			
		||||
    {
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
        eqr(
 | 
			
		||||
            r"associativity: m >>= (\x -> k x >>= h) = (m >>= k) >>= h",
 | 
			
		||||
            T::bind(fa0(), |a| T::bind(g(a), f)),
 | 
			
		||||
            T::bind(T::bind(fa0(), g), f)
 | 
			
		||||
        );
 | 
			
		||||
            T::bind(T::bind(fa0(), g), f),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn seq_can_be_expressed_via_bind<
 | 
			
		||||
@ -314,13 +342,15 @@ mod tests {
 | 
			
		||||
    >(
 | 
			
		||||
        ff0: FF0,
 | 
			
		||||
        fa0: FA0,
 | 
			
		||||
    ) where
 | 
			
		||||
    ) -> R
 | 
			
		||||
    where
 | 
			
		||||
        T::F<B>: Debug + PartialEq,
 | 
			
		||||
    {
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
        eqr(
 | 
			
		||||
            r"seq via bind: m1 <*> m2 = m1 >>= (\x1 -> m2 >>= (\x2 -> pure (x1 x2)))",
 | 
			
		||||
            T::seq(ff0(), fa0()),
 | 
			
		||||
            T::bind(ff0(), |f| T::bind(fa0(), |a| T::pure(f(a))))
 | 
			
		||||
        );
 | 
			
		||||
            T::bind(ff0(), |f| T::bind(fa0(), |a| T::pure(f(a)))),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn fmap_can_be_expressed_via_bind<
 | 
			
		||||
@ -332,195 +362,115 @@ mod tests {
 | 
			
		||||
    >(
 | 
			
		||||
        f: F,
 | 
			
		||||
        fa0: FA0,
 | 
			
		||||
    ) where
 | 
			
		||||
    ) -> R
 | 
			
		||||
    where
 | 
			
		||||
        T::F<B>: Debug + PartialEq,
 | 
			
		||||
    {
 | 
			
		||||
        assert_eq!(T::fmap(f, fa0()), T::bind(fa0(), |a| T::pure(f(a))));
 | 
			
		||||
        eqr(
 | 
			
		||||
            "fmap via bind: fmap f xs  =  xs >>= return . f",
 | 
			
		||||
            T::fmap(f, fa0()),
 | 
			
		||||
            T::bind(fa0(), |a| T::pure(f(a))),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod option_tests {
 | 
			
		||||
    use crate::func::tests;
 | 
			
		||||
pub mod test_suite {
 | 
			
		||||
    use std::fmt::Debug;
 | 
			
		||||
 | 
			
		||||
    use super::{tests::make_none, Functor, OptionClass as T};
 | 
			
		||||
    use super::tests::*;
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn fmap_f_none_is_none() {
 | 
			
		||||
        assert_eq!(T::fmap(|_: ()| (), None), None);
 | 
			
		||||
    pub trait FunctorTestSuite: WeakFunctor {
 | 
			
		||||
        fn sample<A>() -> Vec<Box<dyn Fn(A) -> Self::F<A>>>;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn fmap_f_some_a_is_some_f_a() {
 | 
			
		||||
        assert_eq!(T::fmap(|x| x * x, Some(2)), Some(4));
 | 
			
		||||
    pub fn functor_follows_laws<T: Functor + FunctorTestSuite>() -> R
 | 
			
		||||
    where
 | 
			
		||||
        T::F<i32>: Debug + PartialEq,
 | 
			
		||||
    {
 | 
			
		||||
        let mut res = R::default();
 | 
			
		||||
        for pa in T::sample::<_>() {
 | 
			
		||||
            res += fmap_respects_identity::<T, _, _>(|| pa(2));
 | 
			
		||||
        }
 | 
			
		||||
        for pa in T::sample::<_>() {
 | 
			
		||||
            res += fmap_respects_composition::<T, _, _, _, _, _, _>(|x| x + 5, |x| x + 3, || pa(2));
 | 
			
		||||
        }
 | 
			
		||||
        res
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn fmap_respects_identity() {
 | 
			
		||||
        tests::fmap_respects_identity::<T, _, _>(|| None::<i32>);
 | 
			
		||||
        tests::fmap_respects_identity::<T, _, _>(|| Some(1));
 | 
			
		||||
    pub fn applicative_follows_laws<T: Applicative + FunctorTestSuite>() -> R
 | 
			
		||||
    where
 | 
			
		||||
        T::F<i32>: Debug + PartialEq,
 | 
			
		||||
    {
 | 
			
		||||
        let mut res = functor_follows_laws::<T>();
 | 
			
		||||
        for pa in T::sample::<_>() {
 | 
			
		||||
            res += seq_respects_identity::<T, _, _>(|| pa(2));
 | 
			
		||||
        }
 | 
			
		||||
        for pa in T::sample::<_>() {
 | 
			
		||||
            for pg in T::sample::<_>() {
 | 
			
		||||
                for pf in T::sample::<_>() {
 | 
			
		||||
                    res += seq_respects_composition::<T, _, _, _, _, _, _, _, _>(
 | 
			
		||||
                        || pf(|x| x + 5),
 | 
			
		||||
                        || pg(|x| x + 3),
 | 
			
		||||
                        || pa(2),
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        res += seq_is_homomorphic::<T, _, _, _, _>(|x| x + 3, || 2);
 | 
			
		||||
        for pf in T::sample::<_>() {
 | 
			
		||||
            res += seq_respects_interchange::<T, _, _, _, _, _>(|| pf(|x| x + 3), || 2);
 | 
			
		||||
        }
 | 
			
		||||
        for pa in T::sample::<_>() {
 | 
			
		||||
            for pf in T::sample::<_>() {
 | 
			
		||||
                res += seq_can_be_expressed_via_la2::<T, _, _, _, _, _>(|| pf(|x| x + 3), || pa(2));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        for pa in T::sample::<_>() {
 | 
			
		||||
            res += fmap_can_be_expressed_via_seq::<T, _, _, _, _>(|x| x + 3, || pa(2));
 | 
			
		||||
        }
 | 
			
		||||
        for pa in T::sample::<_>() {
 | 
			
		||||
            for pb in T::sample::<_>() {
 | 
			
		||||
                res += discard_can_be_expressed_via_seq_or_la2::<T, _, _, _, _>(|| pa(2), || pb(2));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        res
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn fmap_respects_composition() {
 | 
			
		||||
        tests::fmap_respects_composition::<T, _, _, _, _, _, _>(
 | 
			
		||||
            |x| x + 5,
 | 
			
		||||
            |x| x + 3,
 | 
			
		||||
            || None::<i32>,
 | 
			
		||||
        );
 | 
			
		||||
        tests::fmap_respects_composition::<T, _, _, _, _, _, _>(|x| x + 5, |x| x + 3, || Some(2));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn replace_none_b_is_none() {
 | 
			
		||||
        assert_eq!(T::replace(None::<i32>, 1), None);
 | 
			
		||||
        assert_eq!(T::void(None::<i32>), None);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn replace_some_a_b_is_some_b() {
 | 
			
		||||
        assert_eq!(T::replace(Some(1), 2), Some(2));
 | 
			
		||||
        assert_eq!(T::void(Some(1)), Some(()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn seq_respects_identity() {
 | 
			
		||||
        tests::seq_respects_identity::<T, _, _>(|| None::<i32>);
 | 
			
		||||
        tests::seq_respects_identity::<T, _, _>(|| Some(1));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn seq_respects_composition() {
 | 
			
		||||
        tests::seq_respects_composition::<T, _, _, _, _, _, _, _, _>(
 | 
			
		||||
            || Some(|x| x + 5),
 | 
			
		||||
            || Some(|x| x + 3),
 | 
			
		||||
            || Some(2),
 | 
			
		||||
        );
 | 
			
		||||
        tests::seq_respects_composition::<T, _, _, _, _, _, _, _, _>(
 | 
			
		||||
            || tests::make_none(Some(|x| x + 5)),
 | 
			
		||||
            || Some(|x| x + 3),
 | 
			
		||||
            || Some(2),
 | 
			
		||||
        );
 | 
			
		||||
        tests::seq_respects_composition::<T, _, _, _, _, _, _, _, _>(
 | 
			
		||||
            || Some(|x| x + 5),
 | 
			
		||||
            || tests::make_none(Some(|x| x + 3)),
 | 
			
		||||
            || Some(2),
 | 
			
		||||
        );
 | 
			
		||||
        tests::seq_respects_composition::<T, _, _, _, _, _, _, _, _>(
 | 
			
		||||
            || Some(|x| x + 5),
 | 
			
		||||
            || Some(|x| x + 3),
 | 
			
		||||
            || None::<i32>,
 | 
			
		||||
        );
 | 
			
		||||
        tests::seq_respects_composition::<T, _, _, _, _, _, _, _, _>(
 | 
			
		||||
            || tests::make_none(Some(|x| x + 5)),
 | 
			
		||||
            || tests::make_none(Some(|x| x + 3)),
 | 
			
		||||
            || None::<i32>,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn seq_is_homomorphic() {
 | 
			
		||||
        tests::seq_is_homomorphic::<T, _, _, _, _>(|x| x + 3, || 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn seq_respects_interchange() {
 | 
			
		||||
        tests::seq_respects_interchange::<T, _, _, _, _, _>(|| Some(|x| x + 3), || 2);
 | 
			
		||||
        tests::seq_respects_interchange::<T, _, _, _, _, _>(|| make_none(Some(|x| x + 3)), || 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn seq_can_be_expressed_via_la2() {
 | 
			
		||||
        tests::seq_can_be_expressed_via_la2::<T, _, _, _, _, _>(|| Some(|x| x + 3), || Some(2));
 | 
			
		||||
        tests::seq_can_be_expressed_via_la2::<T, _, _, _, _, _>(
 | 
			
		||||
            || make_none(Some(|x| x + 3)),
 | 
			
		||||
            || Some(2),
 | 
			
		||||
        );
 | 
			
		||||
        tests::seq_can_be_expressed_via_la2::<T, _, _, _, _, _>(|| Some(|x| x + 3), || None::<i32>);
 | 
			
		||||
        tests::seq_can_be_expressed_via_la2::<T, _, _, _, _, _>(
 | 
			
		||||
            || make_none(Some(|x| x + 3)),
 | 
			
		||||
            || None::<i32>,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn fmap_can_be_expressed_via_seq() {
 | 
			
		||||
        tests::fmap_can_be_expressed_via_seq::<T, _, _, _, _>(|x| x + 3, || Some(2));
 | 
			
		||||
        tests::fmap_can_be_expressed_via_seq::<T, _, _, _, _>(|x| x + 3, || None::<i32>);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn discard_can_be_expressed_via_seq_or_la2() {
 | 
			
		||||
        tests::discard_can_be_expressed_via_seq_or_la2::<T, _, _, _, _>(|| Some(2), || Some(3));
 | 
			
		||||
        tests::discard_can_be_expressed_via_seq_or_la2::<T, _, _, _, _>(|| Some(2), || None::<i32>);
 | 
			
		||||
        tests::discard_can_be_expressed_via_seq_or_la2::<T, _, _, _, _>(|| None::<i32>, || Some(3));
 | 
			
		||||
        tests::discard_can_be_expressed_via_seq_or_la2::<T, _, _, _, _>(
 | 
			
		||||
            || None::<i32>,
 | 
			
		||||
            || None::<i32>,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn bind_respects_left_identity() {
 | 
			
		||||
        tests::bind_respects_left_identity::<T, _, _, _, _>(|x| Some(x + 3), || 2);
 | 
			
		||||
        tests::bind_respects_left_identity::<T, _, _, _, _>(|_| None::<i32>, || 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn bind_respects_right_identity() {
 | 
			
		||||
        tests::bind_respects_right_identity::<T, _, _>(|| Some(1));
 | 
			
		||||
        tests::bind_respects_right_identity::<T, _, _>(|| None::<i32>);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn bind_is_associative() {
 | 
			
		||||
        tests::bind_is_associative::<T, _, _, _, _, _, _>(
 | 
			
		||||
            |x| Some(x + 5),
 | 
			
		||||
            |x| Some(x + 3),
 | 
			
		||||
            || Some(2),
 | 
			
		||||
        );
 | 
			
		||||
        tests::bind_is_associative::<T, _, _, _, _, _, _>(
 | 
			
		||||
            |_| None::<i32>,
 | 
			
		||||
            |x| Some(x + 3),
 | 
			
		||||
            || Some(2),
 | 
			
		||||
        );
 | 
			
		||||
        tests::bind_is_associative::<T, _, _, _, _, _, _>(
 | 
			
		||||
            |x| Some(x + 5),
 | 
			
		||||
            |_| None::<i32>,
 | 
			
		||||
            || Some(2),
 | 
			
		||||
        );
 | 
			
		||||
        tests::bind_is_associative::<T, _, _, _, _, _, _>(
 | 
			
		||||
            |x| Some(x + 5),
 | 
			
		||||
            |x| Some(x + 3),
 | 
			
		||||
            || None::<i32>,
 | 
			
		||||
        );
 | 
			
		||||
        tests::bind_is_associative::<T, _, _, _, _, _, _>(
 | 
			
		||||
            |_| None::<i32>,
 | 
			
		||||
            |_| None::<i32>,
 | 
			
		||||
            || None::<i32>,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn seq_can_be_expressed_via_bind() {
 | 
			
		||||
        tests::seq_can_be_expressed_via_bind::<T, _, _, _, _, _>(|| Some(|x| x + 3), || Some(2));
 | 
			
		||||
        tests::seq_can_be_expressed_via_bind::<T, _, _, _, _, _>(
 | 
			
		||||
            || Some(|x| x + 3),
 | 
			
		||||
            || None::<i32>,
 | 
			
		||||
        );
 | 
			
		||||
        tests::seq_can_be_expressed_via_bind::<T, _, _, _, _, _>(
 | 
			
		||||
            || make_none(Some(|x| x + 3)),
 | 
			
		||||
            || Some(2),
 | 
			
		||||
        );
 | 
			
		||||
        tests::seq_can_be_expressed_via_bind::<T, _, _, _, _, _>(
 | 
			
		||||
            || make_none(Some(|x| x + 3)),
 | 
			
		||||
            || None::<i32>,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn fmap_can_be_expressed_via_bind() {
 | 
			
		||||
        tests::fmap_can_be_expressed_via_bind::<T, _, _, _, _>(|x| x + 3, || Some(2));
 | 
			
		||||
        tests::fmap_can_be_expressed_via_bind::<T, _, _, _, _>(|x| x + 3, || None::<i32>);
 | 
			
		||||
    pub fn monad_follows_laws<T: Monad + FunctorTestSuite>() -> R
 | 
			
		||||
    where
 | 
			
		||||
        T::F<i32>: Debug + PartialEq,
 | 
			
		||||
    {
 | 
			
		||||
        let mut res = applicative_follows_laws::<T>();
 | 
			
		||||
        for pa in T::sample::<_>() {
 | 
			
		||||
            res += bind_respects_left_identity::<T, _, _, _, _>(|x| pa(x + 3), || 2);
 | 
			
		||||
        }
 | 
			
		||||
        for pa in T::sample::<_>() {
 | 
			
		||||
            res += bind_respects_right_identity::<T, _, _>(|| pa(2));
 | 
			
		||||
        }
 | 
			
		||||
        for pa in T::sample::<_>() {
 | 
			
		||||
            for pg in T::sample::<_>() {
 | 
			
		||||
                for pf in T::sample::<_>() {
 | 
			
		||||
                    res += bind_is_associative::<T, _, _, _, _, _, _>(
 | 
			
		||||
                        |x| pf(x + 5),
 | 
			
		||||
                        |x| pg(x + 3),
 | 
			
		||||
                        || pa(2),
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        for pa in T::sample::<_>() {
 | 
			
		||||
            for pf in T::sample::<_>() {
 | 
			
		||||
                res += seq_can_be_expressed_via_bind::<T, _, _, _, _, _>(
 | 
			
		||||
                    || pf(|x| x + 3),
 | 
			
		||||
                    || pa(2),
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        for pa in T::sample::<_>() {
 | 
			
		||||
            res += fmap_can_be_expressed_via_bind::<T, _, _, _, _>(|x| x + 3, || pa(2));
 | 
			
		||||
        }
 | 
			
		||||
        res
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,2 +1,4 @@
 | 
			
		||||
pub mod core;
 | 
			
		||||
pub mod func;
 | 
			
		||||
pub mod optionclass;
 | 
			
		||||
pub mod copy_func;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										117
									
								
								src/optionclass.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								src/optionclass.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,117 @@
 | 
			
		||||
use crate::func::*;
 | 
			
		||||
 | 
			
		||||
pub struct OptionClass;
 | 
			
		||||
 | 
			
		||||
impl WeakFunctor for OptionClass {
 | 
			
		||||
    type F<A> = Option<A>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Functor for OptionClass {
 | 
			
		||||
    fn fmap<A, B, F: FnOnce(A) -> B>(f: F, fa: Self::F<A>) -> Self::F<B> {
 | 
			
		||||
        match fa {
 | 
			
		||||
            Some(a) => Some(f(a)),
 | 
			
		||||
            None => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn replace<A, B>(fa: Self::F<A>, b: B) -> Self::F<B> {
 | 
			
		||||
        match fa {
 | 
			
		||||
            Some(_) => Some(b),
 | 
			
		||||
            None => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ApplicativeSeq for OptionClass {
 | 
			
		||||
    fn seq<A, B, F: FnOnce(A) -> B>(ff: Self::F<F>, fa: Self::F<A>) -> Self::F<B> {
 | 
			
		||||
        match (ff, fa) {
 | 
			
		||||
            (Some(f), Some(a)) => Some(f(a)),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ApplicativeLA2 for OptionClass {
 | 
			
		||||
    fn la2<A, B, C, F: FnOnce(A, B) -> C>(f: F, fa: Self::F<A>, fb: Self::F<B>) -> Self::F<C> {
 | 
			
		||||
        match (fa, fb) {
 | 
			
		||||
            (Some(a), Some(b)) => Some(f(a, b)),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Applicative for OptionClass {
 | 
			
		||||
    fn pure<A>(a: A) -> Self::F<A> {
 | 
			
		||||
        Some(a)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn discard_first<A, B>(fa: Self::F<A>, fb: Self::F<B>) -> Self::F<B> {
 | 
			
		||||
        match fa {
 | 
			
		||||
            Some(_) => fb,
 | 
			
		||||
            None => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn discard_second<A, B>(fa: Self::F<A>, fb: Self::F<B>) -> Self::F<A> {
 | 
			
		||||
        match fb {
 | 
			
		||||
            Some(_) => fa,
 | 
			
		||||
            None => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Monad for OptionClass {
 | 
			
		||||
    fn bind<A, B, F: FnOnce(A) -> Self::F<B>>(fa: Self::F<A>, f: F) -> Self::F<B> {
 | 
			
		||||
        match fa {
 | 
			
		||||
            Some(a) => f(a),
 | 
			
		||||
            None => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn join<A>(ffa: Self::F<Self::F<A>>) -> Self::F<A> {
 | 
			
		||||
        match ffa {
 | 
			
		||||
            Some(Some(a)) => Some(a),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod option_tests {
 | 
			
		||||
    use super::test_suite;
 | 
			
		||||
 | 
			
		||||
    use super::{Functor, OptionClass as T};
 | 
			
		||||
 | 
			
		||||
    impl test_suite::FunctorTestSuite for T {
 | 
			
		||||
        fn sample<A>() -> Vec<Box<dyn Fn(A) -> Self::F<A>>> {
 | 
			
		||||
            vec![Box::new(|_| None), Box::new(|a| Some(a))]
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn fmap_f_none_is_none() {
 | 
			
		||||
        assert_eq!(T::fmap(|_: ()| (), None), None);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn fmap_f_some_a_is_some_f_a() {
 | 
			
		||||
        assert_eq!(T::fmap(|x| x * x, Some(2)), Some(4));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn replace_none_b_is_none() {
 | 
			
		||||
        assert_eq!(T::replace(None::<i32>, 1), None);
 | 
			
		||||
        assert_eq!(T::void(None::<i32>), None);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn replace_some_a_b_is_some_b() {
 | 
			
		||||
        assert_eq!(T::replace(Some(1), 2), Some(2));
 | 
			
		||||
        assert_eq!(T::void(Some(1)), Some(()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn monad_follows_laws() {
 | 
			
		||||
        test_suite::monad_follows_laws::<T>().unwrap();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user