new test sample

This commit is contained in:
AF 2023-03-11 23:16:22 +00:00
parent 445fa00165
commit a76b29abb9
9 changed files with 212 additions and 77 deletions

View File

@ -1,8 +1,9 @@
pub mod classes; pub mod classes;
#[cfg(test)] pub mod copy_func;
pub mod tests;
#[cfg(test)] #[cfg(test)]
pub mod test_suite; pub mod test_suite;
#[cfg(test)]
pub mod tests;
pub trait WeakFunctor { pub trait WeakFunctor {
type F<'a, A>; type F<'a, A>;
@ -73,4 +74,3 @@ pub trait Monad: Applicative {
Self::bind(ffa, |fa| fa) Self::bind(ffa, |fa| fa)
} }
} }

View File

@ -1,2 +1,3 @@
pub mod optionclass; pub mod optionclass;
pub mod futureclass; pub mod futureclass;
pub mod soloclass;

View File

@ -1,7 +1,5 @@
use std::{future::Future, pin::Pin}; use std::{future::Future, pin::Pin};
// use futures::FutureExt;
use futures::join; use futures::join;
use crate::func::*; 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> { 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 { Box::pin(async { join!(fa, fb).1 })
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> { 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 { Box::pin(async { join!(fa, fb).0 })
join!(fa, fb).0
})
} }
} }
impl Monad for FutureClass { 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> { fn bind<'a, A: 'a, B: 'a, F: 'a + FnOnce(A) -> Self::F<'a, B>>(
Box::pin(async{ fa: Self::F<'a, A>,
f(fa.await).await 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 { fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A>
Box::pin(async{ where
ffa.await.await Self::F<'a, A>: 'a,
}) {
Box::pin(async { ffa.await.await })
} }
} }

View File

@ -3,7 +3,7 @@ use crate::func::*;
pub struct OptionClass; pub struct OptionClass;
impl WeakFunctor for OptionClass { impl WeakFunctor for OptionClass {
type F<'_a, A> = Option<A>; type F<'a, A> = Option<A>;
} }
impl Functor for OptionClass { impl Functor for OptionClass {
@ -106,8 +106,12 @@ mod option_tests {
} }
impl test_suite::FunctorTestSuite for T { impl test_suite::FunctorTestSuite for T {
fn sample<'a, A>() -> Vec<Box<dyn Fn(A) -> Self::F<'a, A>>> { fn sample<'a, A: 'a, F: FnMut(&'a dyn Fn(A) -> Self::F<'a, A>)>(mut f: F)
vec![Box::new(|_| None), Box::new(|a| Some(a))] where
Self::F<'a, A>: 'a,
{
f(&|_| None);
f(&|a| Some(a));
} }
} }

View File

@ -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
}
}

View File

@ -1,4 +1,4 @@
use super::func::*; use crate::func::*;
pub trait CopyWeakFunctor { pub trait CopyWeakFunctor {
type CF<'a, A: Copy>; type CF<'a, A: Copy>;

View File

@ -1,88 +1,85 @@
use std::rc::Rc;
use super::{tests::*, *}; use super::{tests::*, *};
pub trait FunctorTestSuite: WeakFunctor + Eqr { pub trait FunctorTestSuite: WeakFunctor + Eqr + 'static {
fn sample<'a, A>() -> Vec<Box<dyn 'a + Fn(A) -> Self::F<'a, A>>>; 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<T: Functor + FunctorTestSuite>() -> R { pub fn functor_follows_laws<T: Functor + FunctorTestSuite>() -> R {
let mut res = R::default(); let mut res = R::default();
for pa in T::sample::<_>() { T::sample(|pa| {
res += fmap_respects_identity::<T, _, _>(|| pa(2)); res += fmap_respects_identity::<T, _, _>(|| pa(2));
} });
for pa in T::sample::<_>() { T::sample(|pa| {
res += fmap_respects_composition::<T, _, _, _, _, _, _>(|x| x + 5, |x| x + 3, || pa(2)); res += fmap_respects_composition::<T, _, _, _, _, _, _>(|x| x + 5, |x| x + 3, || pa(2));
} });
res res
} }
pub fn applicative_follows_laws<T: Applicative + FunctorTestSuite>() -> R { pub fn applicative_follows_laws<T: Applicative + FunctorTestSuite>() -> R {
let mut res = functor_follows_laws::<T>(); let mut res = functor_follows_laws::<T>();
for pa in T::sample::<_>() { T::sample(|pa| {
res += seq_respects_identity::<T, _, _>(|| pa(2)); res += seq_respects_identity::<T, _, _>(|| pa(2));
} });
for pa in T::sample::<_>() { T::sample(|pa| {
for pg in T::sample::<_>() { T::sample(|pg| {
for pf in T::sample::<_>() { T::sample(|pf| {
res += seq_respects_composition::<T, _, _, _, _, _, _, _, _>( res += seq_respects_composition::<T, _, _, _, _, _, _, _, _>(
|| pf(|x| x + 5), || pf(|x| x + 5),
|| pg(|x| x + 3), || pg(|x| x + 3),
|| pa(2), || pa(2),
); );
} })
} })
} });
res += seq_is_homomorphic::<T, _, _, _, _>(|x| x + 3, || 2); res += seq_is_homomorphic::<T, _, _, _, _>(|x| x + 3, || 2);
for pf in T::sample::<_>() { T::sample(|pf| {
res += seq_respects_interchange::<T, _, _, _, _, _>(|| pf(|x| x + 3), || 2); res += seq_respects_interchange::<T, _, _, _, _, _>(|| pf(|x| x + 3), || 2);
} });
for pa in T::sample::<_>() { T::sample(|pa| {
for pf in T::sample::<_>() { T::sample(|pf| {
res += seq_can_be_expressed_via_la2::<T, _, _, _, _, _>(|| pf(|x| x + 3), || pa(2)); res += seq_can_be_expressed_via_la2::<T, _, _, _, _, _>(|| pf(|x| x + 3), || pa(2));
} })
} });
for pa in T::sample::<_>() { T::sample(|pa| {
res += fmap_can_be_expressed_via_seq::<T, _, _, _, _>(|x| x + 3, || pa(2)); res += fmap_can_be_expressed_via_seq::<T, _, _, _, _>(|x| x + 3, || pa(2));
} });
for pa in T::sample::<_>() { T::sample(|pa| {
for pb in T::sample::<_>() { T::sample(|pb| {
res += discard_can_be_expressed_via_seq_or_la2::<T, _, _, _, _>(|| pa(2), || pb(2)); res += discard_can_be_expressed_via_seq_or_la2::<T, _, _, _, _>(|| pa(2), || pb(2));
} })
} });
res res
} }
pub fn monad_follows_laws<T: Monad + FunctorTestSuite>() -> R pub fn monad_follows_laws<T: Monad + FunctorTestSuite>() -> R
where { where {
let mut res = applicative_follows_laws::<T>(); let mut res = applicative_follows_laws::<T>();
for pa in T::sample::<_>() { T::sample(|pa| {
res += bind_respects_left_identity::<T, _, _, _, _>(move |x| pa(x + 3), || 2); res += bind_respects_left_identity::<T, _, _, _, _>(|x| pa(x + 3), || 2);
} });
for pa in T::sample::<_>() { T::sample(|pa| {
res += bind_respects_right_identity::<T, _, _>(move || pa(2)); res += bind_respects_right_identity::<T, _, _>(|| pa(2));
} });
for pa in T::sample::<_>() { T::sample(|pa| {
for pg in T::sample::<_>() { T::sample(|pg| {
let pgrc = Rc::new(pg); T::sample(|pf| {
for pf in T::sample::<_>() {
let pgrci = pgrc.clone();
let pfrc = Rc::new(pf);
res += bind_is_associative::<T, _, _, _, _, _, _>( res += bind_is_associative::<T, _, _, _, _, _, _>(
move |x| pfrc(x + 5), |x| pf(x + 5),
move |x| pgrci(x + 3), |x| pg(x + 3),
|| pa(2), || pa(2),
); );
} })
} })
} });
for pf in T::sample::<_>() { T::sample(|pa| {
for pa in T::sample::<_>() { T::sample(|pf| {
seq_can_be_expressed_via_bind::<T, _, _, _, _, _>(|| pf(|x| x + 3), move || pa(2)); seq_can_be_expressed_via_bind::<T, _, _, _, _, _>(|| pf(|x| x + 3), || pa(2));
} })
} });
for pa in T::sample::<_>() { T::sample(|pa| {
res += fmap_can_be_expressed_via_bind::<T, _, _, _, _>(|x| x + 3, move || pa(2)); res += fmap_can_be_expressed_via_bind::<T, _, _, _, _>(|x| x + 3, || pa(2));
} });
res res
} }

View File

@ -1,3 +1,3 @@
pub mod copy_func;
pub mod core; pub mod core;
pub mod func; pub mod func;
mod xrcs;

63
src/xrcs.rs Normal file
View File

@ -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<T, F: FnOnce(T) -> Option<T>>(f: F, fa: Option<T>) -> Option<T> {
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));
}
}