aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lib.rs12
-rw-r--r--tests/trivial.rs39
-rw-r--r--tests/vector.rs127
-rw-r--r--tests/with_lifetimes.rs60
4 files changed, 232 insertions, 6 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 73469c3..c0783f6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -9,9 +9,9 @@ macro_rules! free {
Free(Box<$f>)
}
impl<$($other_lifetimes,)* $generic> $name<$($other_lifetimes,)* $generic>{
- $v fn lift_f(command : <$f as $crate::higher::Functor<Self>>::Target<$generic>) -> Self{
+ $v fn lift_f(functor : <$f as $crate::higher::Functor<Self>>::Target<$generic>) -> Self{
use $crate::higher::Functor;
- Self::Free(Box::new(command.fmap(|a| Self::Pure(a))))
+ Self::Free(Box::new(functor.fmap(|a| Self::Pure(a))))
}
$v fn retract<'free_macro_reserved_lifetime>(self) -> <$f as $crate::higher::Bind<'free_macro_reserved_lifetime,Self>>::Target<$generic> where $f : $crate::higher::Monad<'free_macro_reserved_lifetime,Self>, <$f as $crate::higher::Bind<'free_macro_reserved_lifetime,Self>>::Target<$generic> : $crate::higher::Pure<$generic> {
@@ -43,7 +43,7 @@ macro_rules! free {
}
}
- impl<'free_macro_reserved_lifetime, $($other_lifetimes,)* A> $crate::higher::Apply<'free_macro_reserved_lifetime, A> for $name<$($other_lifetimes,)* A> where A: 'free_macro_reserved_lifetime + Clone,{
+ impl<'free_macro_reserved_lifetime, $($other_lifetimes,)* A> $crate::higher::Apply<'free_macro_reserved_lifetime, A> for $name<$($other_lifetimes,)* A> where A: 'free_macro_reserved_lifetime + Clone, Self : Clone {
type Target<T> = $name<$($other_lifetimes,)* T> where T:'free_macro_reserved_lifetime;
fn apply<B>(
self,
@@ -80,9 +80,9 @@ macro_rules! free {
Free(Box<$f>)
}
impl<$($other_lifetimes : $a,)* $generic> $name<$($other_lifetimes,)* $generic> where $generic : $a {
- $v fn lift_f(command : <$f as $crate::higher::Functor<$a, Self>>::Target<$generic>) -> Self{
+ $v fn lift_f(functor : <$f as $crate::higher::Functor<$a, Self>>::Target<$generic>) -> Self{
use $crate::higher::Functor;
- Self::Free(Box::new(command.fmap(|a| Self::Pure(a))))
+ Self::Free(Box::new(functor.fmap(|a| Self::Pure(a))))
}
$v fn retract(self) -> <$f as $crate::higher::Bind<$a,Self>>::Target<$generic> where $f : $crate::higher::Monad<$a,Self>, <$f as $crate::higher::Bind<$a,Self>>::Target<$generic> : $crate::higher::Pure<$generic> {
@@ -118,7 +118,7 @@ macro_rules! free {
}
}
- impl<$($other_lifetimes : $a,)* A> $crate::higher::Apply<$a, A> for $name<$($other_lifetimes,)* A> where A: $a + Clone,{
+ impl<$($other_lifetimes : $a,)* A> $crate::higher::Apply<$a, A> for $name<$($other_lifetimes,)* A> where A: $a + Clone, Self : Clone{
type Target<T> = $name<$($other_lifetimes,)* T> where T:$a;
fn apply<B>(
self,
diff --git a/tests/trivial.rs b/tests/trivial.rs
new file mode 100644
index 0000000..5468fe4
--- /dev/null
+++ b/tests/trivial.rs
@@ -0,0 +1,39 @@
+//! A trivial test functor. Not holding any data, so this is basically just a linked list of free-nodes.
+use higher_free_macro::free;
+use higher::{Functor, Bind, Apply};
+
+#[derive(Functor, Clone)]
+struct TrivialFunctor<A>(A);
+
+free!(TrivialFreeMonad<A>, TrivialFunctor<TrivialFreeMonad<A>>);
+
+#[test]
+fn test_trivial_functor() {
+ let m = TrivialFreeMonad::lift_f(TrivialFunctor(37u32));
+ let m = m.fmap(|x| x*2);
+ let m = m.bind(|x| TrivialFreeMonad::Free(Box::new(TrivialFunctor(TrivialFreeMonad::Pure(x)))));
+ let f = TrivialFreeMonad::Pure((|x| x*3).into());
+ let m = m.apply(f);
+ match m {
+ TrivialFreeMonad::Free(b) => {
+ match *b {
+ TrivialFunctor(f) => {
+ match f {
+ TrivialFreeMonad::Free(b) => {
+ match *b {
+ TrivialFunctor(f) => {
+ match f{
+ TrivialFreeMonad::Pure(x) => assert_eq!(x, 37*6),
+ _ => unreachable!()
+ }
+ }
+ }
+ },
+ _ => unreachable!()
+ }
+ }
+ }
+ }
+ _ => unreachable!()
+ }
+} \ No newline at end of file
diff --git a/tests/vector.rs b/tests/vector.rs
new file mode 100644
index 0000000..74cc913
--- /dev/null
+++ b/tests/vector.rs
@@ -0,0 +1,127 @@
+//! Tests if creating a Free Monad for a Vec works. Not sure if this is useful in any way.
+//! It is a nice illustration that Free Monads are tree-like though.
+
+use higher_free_macro::free;
+use higher::{Functor, Bind, Apply};
+
+free!(FreeVec<A>, Vec<FreeVec<A>>);
+
+#[test]
+fn test_vector(){
+ let fv = FreeVec::lift_f(vec![2,3,4]);
+ let fv = fv.fmap(|x| x*2);
+ let fv = fv.bind(|x| if x%3 == 0 {FreeVec::Pure(x)} else {FreeVec::lift_f(vec![x,x+1])});
+ let f = FreeVec::lift_f(vec![(|x| (x as f32) / 3.0) as fn(u32)->f32, (|x| (x+2) as f32) as fn(u32)->f32]);
+ let r = fv.apply(f.fmap(Into::into));
+ match r {
+ FreeVec::Free(v) => {
+ match &**v{
+ [a,b] => {
+ match a {
+ FreeVec::Free(v) => {
+ match &***v {
+ [a,b,c] => {
+ match a {
+ FreeVec::Free(v) => {
+ match &***v {
+ [a,b] => {
+ match a{
+ FreeVec::Free(_) => unreachable!(),
+ FreeVec::Pure(v) => {assert_eq!(4.0f32/3.0f32, *v)}
+ }
+ match b{
+ FreeVec::Free(_) => unreachable!(),
+ FreeVec::Pure(v) => {assert_eq!(5.0f32/3.0f32, *v)}
+ }
+ },
+ _ => unreachable!()
+ }
+ }
+ FreeVec::Pure(_) => unreachable!(),
+ }
+ match b {
+ FreeVec::Free(_) => unreachable!(),
+ FreeVec::Pure(v) => assert_eq!(2.0f32, *v),
+ }
+ match c {
+ FreeVec::Free(v) => {
+ match &***v {
+ [a,b] => {
+ match a{
+ FreeVec::Free(_) => unreachable!(),
+ FreeVec::Pure(v) => {assert_eq!(8.0f32/3.0f32, *v)}
+ }
+ match b{
+ FreeVec::Free(_) => unreachable!(),
+ FreeVec::Pure(v) => {assert_eq!(3.0f32, *v)}
+ }
+ },
+ _ => unreachable!()
+ }
+ }
+ FreeVec::Pure(_) => unreachable!(),
+ }
+ },
+ _ => unreachable!()
+ }
+ }
+ FreeVec::Pure(_) => unreachable!()
+ }
+
+ match b {
+ FreeVec::Free(v) => {
+ match &***v {
+ [a,b,c] => {
+ match a {
+ FreeVec::Free(v) => {
+ match &***v {
+ [a,b] => {
+ match a{
+ FreeVec::Free(_) => unreachable!(),
+ FreeVec::Pure(v) => {assert_eq!(6.0f32, *v)}
+ }
+ match b{
+ FreeVec::Free(_) => unreachable!(),
+ FreeVec::Pure(v) => {assert_eq!(7.0f32, *v)}
+ }
+ },
+ _ => unreachable!()
+ }
+ }
+ FreeVec::Pure(_) => unreachable!(),
+ }
+ match b {
+ FreeVec::Free(_) => unreachable!(),
+ FreeVec::Pure(v) => assert_eq!(8.0f32, *v),
+ }
+ match c {
+ FreeVec::Free(v) => {
+ match &***v {
+ [a,b] => {
+ match a{
+ FreeVec::Free(_) => unreachable!(),
+ FreeVec::Pure(v) => {assert_eq!(10.0f32, *v)}
+ }
+ match b{
+ FreeVec::Free(_) => unreachable!(),
+ FreeVec::Pure(v) => {assert_eq!(11.0f32, *v)}
+ }
+ },
+ _ => unreachable!()
+ }
+ }
+ FreeVec::Pure(_) => unreachable!(),
+ }
+ },
+ _ => unreachable!()
+ }
+ }
+ FreeVec::Pure(_) => unreachable!()
+ }
+ },
+ _ => unreachable!()
+ }
+ }
+ FreeVec::Pure(_) => unreachable!()
+ }
+} \ No newline at end of file
diff --git a/tests/with_lifetimes.rs b/tests/with_lifetimes.rs
new file mode 100644
index 0000000..2e87878
--- /dev/null
+++ b/tests/with_lifetimes.rs
@@ -0,0 +1,60 @@
+//! Test for the case that the Functor the Free Monad is based on has lifetime parameters that do not depend on the
+//! lifetime of the mapping function in the Functor implementation.
+
+use higher_free_macro::free;
+use higher::{Functor, Bind, Apply};
+
+#[derive(Functor, Clone)]
+struct WithLifetimes<'a,'b, A>{
+ s1 : &'a str,
+ s2 : &'b str,
+ next : A
+}
+
+free!(FreeWithLifetimes<'a,'b,A>, WithLifetimes<'a,'b,FreeWithLifetimes<'a,'b,A>>);
+
+fn lifetime_helper<'a,'b>(s1 : &'a str, s2 : &'b str) -> FreeWithLifetimes<'a, 'b, u32>{
+ let fv = FreeWithLifetimes::lift_f(WithLifetimes{ s1, s2, next: 15});
+ fv.fmap(|x| x+1)
+}
+
+#[test]
+fn test_with_lifetimes(){
+ let s1 = "First";
+ let s2 = "Second";
+ let fv = lifetime_helper(s1, s2);
+ let s3 = "Third";
+ let s4 = "Fourth";
+ let fv = fv.bind(|x| FreeWithLifetimes::lift_f(WithLifetimes{ s1: s3, s2: s4, next : x+2}));
+ let s5 = "Fifth";
+ let s6 = "Sixth";
+ let fa = FreeWithLifetimes::lift_f(WithLifetimes{s1: s5, s2: s6, next : (|x| x+3).into()});
+ let fv = fv.apply(fa);
+ match fv {
+ FreeWithLifetimes::Free(v) => {
+ assert_eq!(v.s1, s5);
+ assert_eq!(v.s2, s6);
+ match v.next {
+ FreeWithLifetimes::Free(v) => {
+ assert_eq!(v.s1, s1);
+ assert_eq!(v.s2, s2);
+ match v.next {
+ FreeWithLifetimes::Free(v) => {
+ assert_eq!(v.s1, s3);
+ assert_eq!(v.s2, s4);
+ match v.next {
+ FreeWithLifetimes::Free(_) => unreachable!(),
+ FreeWithLifetimes::Pure(a) => {
+ assert_eq!(a, 21);
+ },
+ }
+ },
+ FreeWithLifetimes::Pure(_) => unreachable!(),
+ }
+ },
+ FreeWithLifetimes::Pure(_) => unreachable!(),
+ }
+ },
+ FreeWithLifetimes::Pure(_) => unreachable!()
+ }
+} \ No newline at end of file