aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lib.rs219
1 files changed, 218 insertions, 1 deletions
diff --git a/src/lib.rs b/src/lib.rs
index ebefa4b..1624053 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -344,7 +344,7 @@ macro_rules! free {
#[cfg(test)]
mod free_monad_tests{
- use higher::{Pure, Functor, Bind, Apply};
+ use higher::{Pure, Functor, Bind, Apply, apply::ApplyFn};
use super::free;
@@ -487,7 +487,224 @@ mod free_monad_tests{
}
}
+ //need Bind and Pure to test retract. This is dumb, but it should fulfill the monad laws:
+ impl<'a, A : 'a, B : 'a> Bind<'a, A> for Conti<'a,A,B> where B : Clone{
+ type Target<T> = Conti<'a,T,B>;
+
+ fn bind<C, F>(self, f: F) -> Self::Target<C> where F: Fn(A) -> Self::Target<C> + 'a {
+ let f = Rc::new(f);
+ let g = f.clone();
+ let l = move |x| f((self.0)(x));
+ let r = move |x| g((self.1)(x));
+ Conti(Rc::new(move |x| (l(x.clone())).0(x)), Rc::new(move |x| (r(x.clone())).1(x)))
+ }
+ }
+ impl<'a, A : 'a, B : 'a> Pure<A> for Conti<'a,A,B> where A : Clone{
+ fn pure(value: A) -> Self {
+ let v2 = value.clone();
+ Conti(Rc::new(move |_| value.clone()), Rc::new(move |_| v2.clone()))
+ }
+ }
+
+ //I really am not certain if the Pure and Bind above are correct. Sooo, why not test those too, while we are at it?
+ #[test]
+ fn test_conti_monad_laws(){
+ let b = |x : u32| {
+ let y = x.clone();
+ Conti::<u32,u32>(Rc::new(move |a| x.clone() + a*2), Rc::new(move |a| y.clone() * a+3))
+ };
+ let t1 = Conti::pure(7u32);
+ let v1 = t1.bind(b);
+ let v2 = b(7u32);
+ assert_eq!((v1.0)(13), (v2.0)(13));
+ assert_eq!((v1.1)(17), (v2.1)(17));
+
+ let c = Conti(Rc::new(|a| 31 + a*5), Rc::new(|b| 32*b+3));
+ let d =c.clone().bind(Conti::pure);
+ assert_eq!((c.0)(3), (d.0)(3));
+ assert_eq!((c.1)(5), (d.1)(5));
+
+ let m = Conti(Rc::new(|a| 32 + (a*2)), Rc::new(|b| 32*b+7));
+ let g = |x : u32| {
+ let y = x.clone();
+ Conti::<u32,u32>(Rc::new(move |a| x.clone()*2 + a), Rc::new(move |a| y.clone() * a+7))
+ };
+ let h = |x : u32| {
+ let y = x.clone();
+ Conti::<u32,u32>(Rc::new(move |a| x.clone() + a), Rc::new(move |a| y.clone() * a+12))
+ };
+
+ let v1 = (m.clone().bind(g.clone())).bind(h.clone());
+ let v2 = m.bind(|a| (g(a).bind(h)));
+ assert_eq!((v1.0)(37), (v2.0)(37));
+ assert_eq!((v1.1)(41), (v2.1)(41));
+
+ //well, looks monadic enough to me. Let's use it for the unit test of retract below.
+ }
+
+
free!(<'a>, FreeConti<'a,A,B>, Conti<'a,FreeConti<'a,A,B>,B>);
+ #[test]
+ fn test_lift_f_lifetime(){
+ let f = FreeConti::lift_f(Conti(Rc::new((|x| x*2) as fn(u32) -> u32), Rc::new((|x| x+5) as fn(u32) -> u32)));
+ match f {
+ FreeConti::Free(m) => {
+ match (m.0)(4){
+ FreeConti::Pure(v) => assert_eq!(v, 8),
+ _ => unreachable!()
+ }
+ match (m.1)(4){
+ FreeConti::Pure(v) => assert_eq!(v, 9),
+ _ => unreachable!()
+ }
+ },
+ _ => unreachable!()
+ }
+ }
+
+ #[test]
+ fn test_retract_lifetime(){
+ let f = FreeConti::lift_f(Conti(Rc::new((|x| x*2) as fn(u32) -> u32), Rc::new((|x| x+5) as fn(u32) -> u32)));
+ let r = f.retract();
+ assert_eq!((r.0)(4), 8);
+ assert_eq!((r.1)(4), 9);
+ }
+
+ #[test]
+ fn test_fmap_lifetime(){
+ let c = Conti(Rc::new(|x : u32| (x as i32)*3+2), Rc::new(|x| ((x as i32)+2)*5));
+ let f = FreeConti::lift_f(c);
+ let f = f.fmap(|x : i32| (x as f32)*0.25f32);
+ match f {
+ FreeConti::Free(f) => {
+ let l = (f.0)(7);
+ match l {
+ FreeConti::Pure(v) => {
+ assert_eq!(v, 5.75f32);
+ },
+ _ => unreachable!()
+ }
+ let r = (f.1)(7);
+ match r {
+ FreeConti::Pure(v) => {
+ assert_eq!(v, 11.25f32);
+ },
+ _ => unreachable!()
+ }
+ },
+ _ => unreachable!()
+ }
+ }
+
+ #[test]
+ fn test_pure_lifetime(){
+ let f : FreeConti<_,()> = FreeConti::pure(27);
+ match f {
+ FreeConti::Pure(v) => assert_eq!(v, 27),
+ FreeConti::Free(_) => unreachable!(),
+ }
+ }
+
+ #[test]
+ fn test_bind_lifetime(){
+ let c = Conti(Rc::new(|x : u32| (x as i32)*3+2), Rc::new(|x| ((x as i32)+2)*5));
+ let f = FreeConti::lift_f(c);
+ let f = f.bind(|y| {
+ let z = y.clone();
+ FreeConti::lift_f(Conti(Rc::new(move |x| (x as f32)*0.25f32 + (y as f32)), Rc::new(move |x| (x as f32) * 0.5f32 - (z as f32))))
+ });
+ match f {
+ FreeConti::Free(f) => {
+ let l = (f.0)(4);
+ match l {
+ FreeConti::Free(f) => {
+ //14i32
+ let l = (f.0)(5);
+ match l {
+ FreeConti::Pure(v) => assert_eq!(v, 15.25f32),
+ FreeConti::Free(_) => unreachable!(),
+ }
+ let r = (f.1)(5);
+ match r{
+ FreeConti::Pure(v) => assert_eq!(v, -11.5f32),
+ FreeConti::Free(_) => unreachable!(),
+ }
+ },
+ FreeConti::Pure(_) => unreachable!(),
+ }
+ let r = (f.1)(4);
+ match r {
+ FreeConti::Free(f) => {
+ //30i32
+ let l = (f.0)(5);
+ match l {
+ FreeConti::Pure(v) => assert_eq!(v, 31.25f32),
+ FreeConti::Free(_) => unreachable!(),
+ }
+ let r = (f.1)(5);
+ match r {
+ FreeConti::Pure(v) => assert_eq!(v, -27.5f32),
+ FreeConti::Free(_) => unreachable!(),
+ }
+ },
+ FreeConti::Pure(_) => unreachable!(),
+ }
+ },
+ _ => unreachable!()
+ }
+ }
+
+ #[test]
+ fn test_apply_lifetime(){
+ //oh, god, please no.
+ let m = FreeConti::lift_f(Conti(Rc::new(|x : u32| (x as i32)*3+2), Rc::new(|x| ((x as i32)+2)*5)));
+ let f = FreeConti::lift_f(Conti(Rc::new(|x : u32| -> ApplyFn<i32,f32> {
+ (move |y : i32| (y + (x as i32)) as f32).into()
+ }), Rc::new(|x : u32| {
+ (move |y : i32| (y*(x as i32)) as f32).into()
+ })));
+ //make it stop!
+ let m = m.apply(f);
+
+ match m {
+ FreeConti::Free(m) => {
+ let l = (m.0)(5u32);
+ match l {
+ FreeConti::Free(m) => {
+ let l = (m.0)(7u32);
+ match l {
+ FreeConti::Pure(v) => assert_eq!(v,28f32),
+ FreeConti::Free(_) => unreachable!(),
+ }
+ let r = (m.1)(7u32);
+ match r {
+ FreeConti::Pure(v) => assert_eq!(v,50f32),
+ FreeConti::Free(_) => unreachable!(),
+ }
+ },
+ FreeConti::Pure(_) => unreachable!(),
+ }
+ let r = (m.1)(5u32);
+ match r {
+ FreeConti::Free(m) => {
+ let l = (m.0)(7u32);
+ match l {
+ FreeConti::Pure(v) => assert_eq!(v,(5f32*23f32)),
+ FreeConti::Free(_) => unreachable!(),
+ }
+ let r = (m.1)(7u32);
+ match r {
+ FreeConti::Pure(v) => assert_eq!(v, 5f32*45f32),
+ FreeConti::Free(_) => unreachable!(),
+ }
+ },
+ FreeConti::Pure(_) => unreachable!(),
+ }
+ },
+ _ => unreachable!()
+ }
+ //let's never speak of this again.
+ }
} \ No newline at end of file