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));
+ }
+}