From a76b29abb9e14158b711a3d0cfe0dbb9e31a48eb Mon Sep 17 00:00:00 2001 From: timofey Date: Sat, 11 Mar 2023 23:16:22 +0000 Subject: [PATCH] new test sample --- src/func.rs | 6 +- src/func/classes.rs | 1 + src/func/classes/futureclass.rs | 28 ++++----- src/func/classes/optionclass.rs | 10 +++- src/func/classes/soloclass.rs | 74 +++++++++++++++++++++++ src/{ => func}/copy_func.rs | 2 +- src/func/test_suite.rs | 103 ++++++++++++++++---------------- src/lib.rs | 2 +- src/xrcs.rs | 63 +++++++++++++++++++ 9 files changed, 212 insertions(+), 77 deletions(-) create mode 100644 src/func/classes/soloclass.rs rename src/{ => func}/copy_func.rs (99%) create mode 100644 src/xrcs.rs diff --git a/src/func.rs b/src/func.rs index 9b2b7a0..5fe83ed 100644 --- a/src/func.rs +++ b/src/func.rs @@ -1,8 +1,9 @@ pub mod classes; -#[cfg(test)] -pub mod tests; +pub mod copy_func; #[cfg(test)] pub mod test_suite; +#[cfg(test)] +pub mod tests; pub trait WeakFunctor { type F<'a, A>; @@ -73,4 +74,3 @@ pub trait Monad: Applicative { Self::bind(ffa, |fa| fa) } } - diff --git a/src/func/classes.rs b/src/func/classes.rs index 6f0ff5e..c3e60c2 100644 --- a/src/func/classes.rs +++ b/src/func/classes.rs @@ -1,2 +1,3 @@ pub mod optionclass; pub mod futureclass; +pub mod soloclass; diff --git a/src/func/classes/futureclass.rs b/src/func/classes/futureclass.rs index 6307f41..bc8d351 100644 --- a/src/func/classes/futureclass.rs +++ b/src/func/classes/futureclass.rs @@ -1,7 +1,5 @@ use std::{future::Future, pin::Pin}; -// use futures::FutureExt; - use futures::join; use crate::func::*; @@ -56,28 +54,26 @@ impl Applicative for FutureClass { } fn discard_first<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, fb: Self::F<'a, B>) -> Self::F<'a, B> { - Box::pin(async { - join!(fa, fb).1 - }) + Box::pin(async { join!(fa, fb).1 }) } fn discard_second<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, fb: Self::F<'a, B>) -> Self::F<'a, A> { - Box::pin(async { - join!(fa, fb).0 - }) + Box::pin(async { join!(fa, fb).0 }) } } impl Monad for FutureClass { - fn bind<'a, A: 'a, B: 'a, F: 'a + FnOnce(A) -> Self::F<'a, B>>(fa: Self::F<'a, A>, f: F) -> Self::F<'a, B> { - Box::pin(async{ - f(fa.await).await - }) + fn bind<'a, A: 'a, B: 'a, F: 'a + FnOnce(A) -> Self::F<'a, B>>( + fa: Self::F<'a, A>, + f: F, + ) -> Self::F<'a, B> { + Box::pin(async { f(fa.await).await }) } - fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> where Self::F<'a, A>: 'a { - Box::pin(async{ - ffa.await.await - }) + fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> + where + Self::F<'a, A>: 'a, + { + Box::pin(async { ffa.await.await }) } } diff --git a/src/func/classes/optionclass.rs b/src/func/classes/optionclass.rs index 59e7372..56b4642 100644 --- a/src/func/classes/optionclass.rs +++ b/src/func/classes/optionclass.rs @@ -3,7 +3,7 @@ use crate::func::*; pub struct OptionClass; impl WeakFunctor for OptionClass { - type F<'_a, A> = Option; + type F<'a, A> = Option; } impl Functor for OptionClass { @@ -106,8 +106,12 @@ mod option_tests { } impl test_suite::FunctorTestSuite for T { - fn sample<'a, A>() -> Vec Self::F<'a, A>>> { - vec![Box::new(|_| None), Box::new(|a| Some(a))] + fn sample<'a, A: 'a, F: FnMut(&'a dyn Fn(A) -> Self::F<'a, A>)>(mut f: F) + where + Self::F<'a, A>: 'a, + { + f(&|_| None); + f(&|a| Some(a)); } } diff --git a/src/func/classes/soloclass.rs b/src/func/classes/soloclass.rs new file mode 100644 index 0000000..1a6c926 --- /dev/null +++ b/src/func/classes/soloclass.rs @@ -0,0 +1,74 @@ +use crate::func::*; + +pub struct SoloClass; + +impl WeakFunctor for SoloClass { + type F<'a, A> = A; +} + +impl Functor for SoloClass { + fn fmap<'a, A: 'a, B: 'a, F: 'a + FnOnce(A) -> B>(f: F, fa: Self::F<'a, A>) -> Self::F<'a, B> { + f(fa) + } + + fn replace<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, b: B) -> Self::F<'a, B> { + drop(fa); + b + } + + fn void<'a, A: 'a>(fa: Self::F<'a, A>) -> Self::F<'a, ()> { + drop(fa); + () + } +} + +impl ApplicativeSeq for SoloClass { + fn seq<'a, A: 'a, B: 'a, F: 'a + FnOnce(A) -> B>( + ff: Self::F<'a, F>, + fa: Self::F<'a, A>, + ) -> Self::F<'a, B> { + ff(fa) + } +} + +impl ApplicativeLA2 for SoloClass { + fn la2<'a, A: 'a, B: 'a, C: 'a, F: 'a + FnOnce(A, B) -> C>( + f: F, + fa: Self::F<'a, A>, + fb: Self::F<'a, B>, + ) -> Self::F<'a, C> { + f(fa, fb) + } +} + +impl Applicative for SoloClass { + fn pure<'a, A: 'a>(a: A) -> Self::F<'a, A> { + a + } + + fn discard_first<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, fb: Self::F<'a, B>) -> Self::F<'a, B> { + drop(fa); + fb + } + + fn discard_second<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, fb: Self::F<'a, B>) -> Self::F<'a, A> { + drop(fb); + fa + } +} + +impl Monad for SoloClass { + fn bind<'a, A: 'a, B: 'a, F: 'a + FnOnce(A) -> Self::F<'a, B>>( + fa: Self::F<'a, A>, + f: F, + ) -> Self::F<'a, B> { + f(fa) + } + + fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> + where + Self::F<'a, A>: 'a, + { + ffa + } +} diff --git a/src/copy_func.rs b/src/func/copy_func.rs similarity index 99% rename from src/copy_func.rs rename to src/func/copy_func.rs index a053753..1d63dd5 100644 --- a/src/copy_func.rs +++ b/src/func/copy_func.rs @@ -1,4 +1,4 @@ -use super::func::*; +use crate::func::*; pub trait CopyWeakFunctor { type CF<'a, A: Copy>; diff --git a/src/func/test_suite.rs b/src/func/test_suite.rs index 6d7a925..cfaa8ee 100644 --- a/src/func/test_suite.rs +++ b/src/func/test_suite.rs @@ -1,88 +1,85 @@ -use std::rc::Rc; - use super::{tests::*, *}; -pub trait FunctorTestSuite: WeakFunctor + Eqr { - fn sample<'a, A>() -> Vec Self::F<'a, A>>>; +pub trait FunctorTestSuite: WeakFunctor + Eqr + 'static { + fn sample<'a, A: 'a, F: FnMut(&'a dyn Fn(A) -> Self::F<'a, A>)>(f: F) + where + Self::F<'a, A>: 'a; } pub fn functor_follows_laws() -> R { let mut res = R::default(); - for pa in T::sample::<_>() { + T::sample(|pa| { res += fmap_respects_identity::(|| pa(2)); - } - for pa in T::sample::<_>() { + }); + T::sample(|pa| { res += fmap_respects_composition::(|x| x + 5, |x| x + 3, || pa(2)); - } + }); res } pub fn applicative_follows_laws() -> R { let mut res = functor_follows_laws::(); - for pa in T::sample::<_>() { + T::sample(|pa| { res += seq_respects_identity::(|| pa(2)); - } - for pa in T::sample::<_>() { - for pg in T::sample::<_>() { - for pf in T::sample::<_>() { + }); + T::sample(|pa| { + T::sample(|pg| { + T::sample(|pf| { res += seq_respects_composition::( || pf(|x| x + 5), || pg(|x| x + 3), || pa(2), ); - } - } - } + }) + }) + }); res += seq_is_homomorphic::(|x| x + 3, || 2); - for pf in T::sample::<_>() { + T::sample(|pf| { res += seq_respects_interchange::(|| pf(|x| x + 3), || 2); - } - for pa in T::sample::<_>() { - for pf in T::sample::<_>() { + }); + T::sample(|pa| { + T::sample(|pf| { res += seq_can_be_expressed_via_la2::(|| pf(|x| x + 3), || pa(2)); - } - } - for pa in T::sample::<_>() { + }) + }); + T::sample(|pa| { res += fmap_can_be_expressed_via_seq::(|x| x + 3, || pa(2)); - } - for pa in T::sample::<_>() { - for pb in T::sample::<_>() { + }); + T::sample(|pa| { + T::sample(|pb| { res += discard_can_be_expressed_via_seq_or_la2::(|| pa(2), || pb(2)); - } - } + }) + }); res } pub fn monad_follows_laws() -> R where { let mut res = applicative_follows_laws::(); - for pa in T::sample::<_>() { - res += bind_respects_left_identity::(move |x| pa(x + 3), || 2); - } - for pa in T::sample::<_>() { - res += bind_respects_right_identity::(move || pa(2)); - } - for pa in T::sample::<_>() { - for pg in T::sample::<_>() { - let pgrc = Rc::new(pg); - for pf in T::sample::<_>() { - let pgrci = pgrc.clone(); - let pfrc = Rc::new(pf); + T::sample(|pa| { + res += bind_respects_left_identity::(|x| pa(x + 3), || 2); + }); + T::sample(|pa| { + res += bind_respects_right_identity::(|| pa(2)); + }); + T::sample(|pa| { + T::sample(|pg| { + T::sample(|pf| { res += bind_is_associative::( - move |x| pfrc(x + 5), - move |x| pgrci(x + 3), + |x| pf(x + 5), + |x| pg(x + 3), || pa(2), ); - } - } - } - for pf in T::sample::<_>() { - for pa in T::sample::<_>() { - seq_can_be_expressed_via_bind::(|| pf(|x| x + 3), move || pa(2)); - } - } - for pa in T::sample::<_>() { - res += fmap_can_be_expressed_via_bind::(|x| x + 3, move || pa(2)); - } + }) + }) + }); + T::sample(|pa| { + T::sample(|pf| { + seq_can_be_expressed_via_bind::(|| pf(|x| x + 3), || pa(2)); + }) + }); + T::sample(|pa| { + res += fmap_can_be_expressed_via_bind::(|x| x + 3, || pa(2)); + }); res } diff --git a/src/lib.rs b/src/lib.rs index e255570..5ed316e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,3 @@ -pub mod copy_func; pub mod core; pub mod func; +mod xrcs; diff --git a/src/xrcs.rs b/src/xrcs.rs new file mode 100644 index 0000000..a3326e4 --- /dev/null +++ b/src/xrcs.rs @@ -0,0 +1,63 @@ +/// Solutions to some exercises by Alisa Feistel. +/// Included here for their relevance to the whole Monad theme. +#[cfg(test)] +mod tests { + fn bind Option>(f: F, fa: Option) -> Option { + match fa { + Some(a) => f(a), + None => None, + } + } + + #[test] + fn test() { + assert_eq!(bind(|x: i32| Some(x + 3), Some(2)), Some(5)); + assert_eq!(bind(|x: i32| Some(x + 3), None), None); + assert_eq!(bind(|_: i32| None, Some(2)), None); + assert_eq!(bind(|_: i32| None, None), None); + + assert_eq!(bind(|x: &str| Some(x), Some("apple")), Some("apple")); + assert_eq!( + bind(|_: &str| Some("banana"), Some("apple")), + Some("banana") + ); + + let banana = "banana".to_string(); + assert_eq!( + bind(|_: &str| Some(banana.as_str()), Some("apple")), + Some("banana") + ); + + let banana = "banana".to_string(); + assert_eq!( + bind(|_: String| Some(banana), Some("apple".to_string())), + Some("banana".to_string()) + ); + } +} + +#[cfg(test)] +mod reftests { + fn refbind<'a, 'b, T: 'a + ?Sized, F: 'a + Fn(&'a T) -> Option<&'b T>>( + f: F, + fa: Option<&'a T>, + ) -> Option<&'b T> { + match fa { + Some(a) => f(a), + None => None, + } + } + + #[test] + fn test() { + let apple = "apple".to_string(); + let banana = "banana".to_string(); + assert_eq!( + refbind(|_: &String| Some(&banana), Some(&apple)), + Some(&banana) + ); + + let banana = "banana"; + assert_eq!(refbind(|_: &str| Some(banana), Some("apple")), Some(banana)); + } +}