From cebdd3be32d50be379663e92d4428e6bba19ba51 Mon Sep 17 00:00:00 2001 From: Andreas Grois Date: Sun, 2 Apr 2023 21:59:22 +0200 Subject: Run cargo fmt I think readability was better before that... --- src/lib.rs | 414 +++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 225 insertions(+), 189 deletions(-) (limited to 'src/lib.rs') diff --git a/src/lib.rs b/src/lib.rs index da8d67c..0b3abd2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,51 +1,51 @@ #![deny(clippy::pedantic)] #![deny(clippy::all)] //! A macro that uses the traits from the [higher] crate and generates a Free [`Monad`][higher::Monad] type for a given [`Functor`][higher::Functor]. -//! +//! //! This is a port of the Control.Monad.Free part of the ["free" Haskell package](https://hackage.haskell.org/package/free) by Edward Kmett. -//! +//! //! # What is a Free Monad? //! A Free Monad is the left-adjoint to the Forget-Functor from the category of Monads into the category of Endofunctors. -//! +//! //! From a programmer's perspective, however, it is a nifty way to create a [`Monad`][higher::Monad], that is "based" on a given [`Functor`][higher::Functor] //! and does not impose any additional structure beyond the [Monad Laws](https://wiki.haskell.org/Monad_laws). -//! +//! //! The structure of the Free [`Monad`][higher::Monad] is defined by the underlying [`Functor`][higher::Functor]. //! For instance, if the underlying [`Functor`][higher::Functor] is a [`Vec`], the corresponding Free [`Monad`][higher::Monad] will be a linked tree. //! If the underlying [`Functor`][higher::Functor] is an [`Option`], the corresponding Free [`Monad`][higher::Monad] is a linked list. //! And so on, and so forth. -//! +//! //! There are many use cases for such a data structure, the most well known one is the creation of embedded //! [Domain Specific Languages](https://en.wikipedia.org/wiki/Domain-specific_language) (eDSLs). //! Going into detail would go beyond the scope of this documentation, however. Please check out Nikolay Yakimov's //! [Introduction to Free Monads](https://serokell.io/blog/introduction-to-free-monads) for that. -//! +//! //! There is also a [blog post about the development of this macro](https://www.grois.info/posts/2023-03/2023-03-11-adventures-with-free-monads-and-higher.xhtml), //! that presents a simple (but inexact) mental picture //! (by means of actual [pictures](https://www.grois.info/posts/2023-03/2023-03-11-adventures-with-free-monads-and-higher.xhtml#ugly_drawings)) //! of how the different [`Monad`][higher::Monad] operations (bind, fmap, pure, apply) work on Free Monads. -//! +//! //! # How to use the macro? -//! +//! //! For details, please see the documentation of the [free] macro. //! In short, the syntax is either `free!(FreeMonadTypeName<'a, A>, FunctorItsBasedOn>)`, //! or, if the lifetime of the Free Monad depends on the lifetime of the function passed to the Functor's fmap function, //! `free!(<'a>, FreeMonadTypeName<'a,A>, FunctorItsBasedOn<'a,FreeMonadTypeName<'a,A>>)`, where `'a` is the affected lifetime. -//! +//! //! # Examples //! The project's repository contains a folder named "examples", which at the moment contains a tiny text adventure that shows how such a game //! could be implemented with Free Monads. The example highlights both, features and (current) limitations of Free Monads in Rust. -//! +//! //! In addition, there is the "tests" folder, which contains integration tests, that show the syntax of the `free!()` macro in action. -//! +//! //! # Why a Macro? //! Until [non-lifetime binders](https://github.com/rust-lang/rust/issues/108185) become stable, this seems to be the easiest way. //! In generic code, the type signature would be `enum Free where F : Functor>`. If one now wants to implement the [`Functor`][higher::Functor] //! trait for this, it is not really possible to express the `Target = Free>>>` generic associated type. -//! +//! //! See the [blog post about this crate](https://www.grois.info/posts/2023-03/2023-03-11-adventures-with-free-monads-and-higher.xhtml) //! for a more detailed explanation. -//! +//! //! # A word of warning: //! This crate should be considered a proof-of-concept. Its memory complexity is horrendous, and the performance of the Free Monad's [`Apply`][higher::Apply] //! implementation can only be described as abysmal due to its reliance on deep copies. @@ -57,42 +57,42 @@ pub extern crate higher; /// The macro that generates a Free [`Monad`][higher::Monad] type for a given [`Functor`][higher::Functor]. -/// +/// /// To declare a Free [`Monad`][higher::Monad] over a [`Functor`][higher::Functor] named `Funky`, the syntax would be `free!(FreeFunky, Funky>)`. /// This declares an enum named `FreeFunky`, and implements all traits needed for it to be a [`Monad`][higher::Monad]. -/// +/// /// # Restrictions /// It is currently not supported to create a Free Monad for a Functor that does not implement [`Clone`]. This is because it is in general not /// possible to implement [`Apply`][higher::Apply] for a non-cloneable Free Monad, and because of how Rust resolves trait bounds in recursive types. -/// +/// /// In addition, for the result to actually be a [`Monad`][higher::Monad], the `Pure` type (the type the Free Monad is generic over) needs to support [`Clone`] /// too. This is again because of the requirement of [`Apply`][higher::Apply], which in turn is a requirement of [`Monad`][higher::Monad]. However, -/// it is typically not necessary to have a fully fledged [`Monad`][higher::Monad]. In most use cases, it's enough to have +/// it is typically not necessary to have a fully fledged [`Monad`][higher::Monad]. In most use cases, it's enough to have /// [`Functor`][higher::Functor] + [`Bind`][higher::Bind] + [`Pure`][higher::Pure]. -/// +/// /// The Free Monad type is implemented recursively. It is therefore akin to a linked tree, with all the respective performance implications. -/// +/// /// Furthermore, the implementation of [`Apply`][higher::Apply] creates a potentially high number of deep copies of the `self` parameter. -/// It should therefore be avoided, unless one really needs its +/// It should therefore be avoided, unless one really needs its /// [tree-merging behaviour](https://www.grois.info/posts/2023-03/2023-03-11-adventures-with-free-monads-and-higher.xhtml#ugly_apply_drawing). -/// +/// /// # Usage /// As stated above, the syntax to create a Free Monad is usually to call the macro with the desired Free Monad type as first, /// and the [`Functor`][higher::Functor] it should be based on as second parameter. -/// +/// /// For example, a Free Monad based on [`Option`] could simply be created like this: /// ``` /// # #[macro_use] extern crate higher_free_macro; /// # use higher_free_macro::higher::*; /// free!(FreeOption, Option>); /// ``` -/// +/// /// The type created by this is indeed a Monad, as long as the wrapped type is [`Clone`]: /// ``` /// # #[macro_use] extern crate higher_free_macro; /// # use higher_free_macro::higher::*; /// free!(FreeOption, Option>); -/// +/// /// fn returns_a_monad<'a, A>(a : A) -> impl Monad<'a,A> where A : Clone + 'a { /// FreeOption::Pure(a) /// } @@ -111,25 +111,25 @@ pub extern crate higher; /// FreeOption::Pure(a) /// } /// ``` -/// +/// /// That said, the macro also supports multiple generic parameters. The parameter for which the traits will be implemented is the first generic parameter /// of the to-be-created Free Monad type. For instance, a Free Monad based on [`Result`] would be: /// ``` /// # #[macro_use] extern crate higher_free_macro; /// # use higher_free_macro::higher::*; /// free!(FreeResult, Result,E>); -/// -/// fn returns_a_monad<'a, A, E>(r : Result) -> impl Monad<'a,A> +/// +/// fn returns_a_monad<'a, A, E>(r : Result) -> impl Monad<'a,A> /// where A : Clone + 'a, E : Clone /// { /// FreeResult::lift_f(r) /// } /// ``` -/// +/// /// Furthermore, the use case that the lifetime of the Free Monad depends on the lifetime of the mapping functions is supported too. -/// This is particularly useful, because it enables the usage of (non-constant) continuation functions, what is a requirement for +/// This is particularly useful, because it enables the usage of (non-constant) continuation functions, what is a requirement for /// using the Free Monad for an embedded Domain Specific Language (eDSL). -/// +/// /// Such a [`Functor`][higher::Functor] could for instance look like this: /// ``` /// # #[macro_use] extern crate higher_free_macro; @@ -143,7 +143,7 @@ pub extern crate higher; /// } /// } /// ``` -/// +/// /// Sadly, the macro syntax is a bit more convoluted in this case. The relevant lifetime has to be stated explicitly as the first parameter, like this: /// ``` /// # #[macro_use] extern crate higher_free_macro; @@ -158,7 +158,7 @@ pub extern crate higher; /// # } /// # } /// ``` -/// +/// /// # Generated Functions /// In addition to the trait implementations for [`Bind`][higher::Bind], [`Functor`][higher::Functor], [`Apply`][higher::Apply] and [`Pure`][higher::Pure], /// the macro also generates associated functions for the Free Monad type. These functions are: @@ -168,11 +168,11 @@ pub extern crate higher; /// A concrete example will make this more clear. Let's take our `FreeOption` example from above. In this case, the signatures are /// `fn lift_f(functor : Option) -> FreeOption` and /// `fn retract(self : FreeOption) -> Option` -/// +/// /// `lift_f()` converts a base Functor into the corresponding Free Monad, meaning that the Functor gets wrapped in `Free`, and the value it holds gets /// mapped into a `Pure`. The (simplified for readability) formula is: /// `Self::Free(functor.fmap(|a| Self::Pure(a)))` -/// +/// /// `retract()` is the left-inverse of `lift_f()`. `|x| retract(lift_f(x))` is (ignoring type coercion) equivalent to [`identity`][std::convert::identity]: /// ``` /// # #[macro_use] extern crate higher_free_macro; @@ -222,7 +222,7 @@ macro_rules! free { } } } - + impl<'free_macro_reserved_lifetime, $($other_lifetimes,)* $generic $(,$other_generics)*> $crate::higher::Functor<'free_macro_reserved_lifetime,$generic> for $name<$($other_lifetimes,)* $generic $(,$other_generics)*> { type Target = $name<$($other_lifetimes,)* FreeMacroReservedType $(,$other_generics)*>; fn fmap(self, f: FreeMacroReservedType2) -> Self::Target where FreeMacroReservedType2: Fn($generic) -> FreeMacroReservedType + 'free_macro_reserved_lifetime{ @@ -294,10 +294,10 @@ macro_rules! free { } } } - + impl<$($other_lifetimes : $a,)* $generic $(,$other_generics)*> $crate::higher::Functor<$a,$generic> for $name<$($other_lifetimes,)* $generic $(,$other_generics)*> where $generic : $a $(,$other_generics : $a)* { type Target = $name<$($other_lifetimes,)* FreeMacroReservedType $(,$other_generics)*>; - fn fmap(self, f: F) -> Self::Target + fn fmap(self, f: F) -> Self::Target where F: Fn($generic) -> FreeMacroReservedType + $a { fn __fmap_impl<$($other_lifetimes : $a,)* $generic $(,$other_generics)*, FreeMacroReservedType, F>(s : $name<$($other_lifetimes,)* $generic $(,$other_generics)*>, f : std::rc::Rc) -> $name<$($other_lifetimes,)* FreeMacroReservedType $(,$other_generics)*> where $generic : $a $(,$other_generics : $a)*, F: Fn($generic) -> FreeMacroReservedType + $a{ @@ -352,139 +352,142 @@ macro_rules! free { } #[cfg(test)] -mod free_monad_tests{ - use higher::{Pure, Functor, Bind, Apply, apply::ApplyFn}; +mod free_monad_tests { + use higher::{apply::ApplyFn, Apply, Bind, Functor, Pure}; use super::free; - + free!(FreeVec, Vec>); #[test] - fn test_lift_f_no_lifetime(){ - let free = FreeVec::lift_f(vec![1,2,3]); + fn test_lift_f_no_lifetime() { + let free = FreeVec::lift_f(vec![1, 2, 3]); match free { - FreeVec::Free(vector) => { - match &**vector { - [FreeVec::Pure(a),FreeVec::Pure(b),FreeVec::Pure(c)] => { - assert_eq!(vec![*a,*b,*c], vec![1,2,3]); - }, - _ => unreachable!() + FreeVec::Free(vector) => match &**vector { + [FreeVec::Pure(a), FreeVec::Pure(b), FreeVec::Pure(c)] => { + assert_eq!(vec![*a, *b, *c], vec![1, 2, 3]); } + _ => unreachable!(), }, - FreeVec::Pure(_) => unreachable!() + FreeVec::Pure(_) => unreachable!(), } } #[test] - fn test_retract_no_lifetime(){ - let f = FreeVec::lift_f(vec![1,2,3]); + fn test_retract_no_lifetime() { + let f = FreeVec::lift_f(vec![1, 2, 3]); let v = f.retract(); - assert_eq!(v, vec![1,2,3]); + assert_eq!(v, vec![1, 2, 3]); } #[test] - fn test_pure_no_lifetime(){ + fn test_pure_no_lifetime() { let f = FreeVec::pure(3); match f { - FreeVec::Pure(v) => assert_eq!(v,3), + FreeVec::Pure(v) => assert_eq!(v, 3), FreeVec::Free(_) => unreachable!(), } } #[test] - fn test_fmap_no_lifetime(){ - let f = FreeVec::lift_f(vec![1,2,3]); - let f = f.fmap(|x| (f64::from(x))/2.0); + fn test_fmap_no_lifetime() { + let f = FreeVec::lift_f(vec![1, 2, 3]); + let f = f.fmap(|x| (f64::from(x)) / 2.0); match f { - FreeVec::Free(f) => { - match &**f{ - [FreeVec::Pure(a), FreeVec::Pure(b), FreeVec::Pure(c)] => { - assert_eq!(vec![0.5f64, 1f64, 1.5f64], vec![*a,*b,*c]); - }, - _ => unreachable!() + FreeVec::Free(f) => match &**f { + [FreeVec::Pure(a), FreeVec::Pure(b), FreeVec::Pure(c)] => { + assert_eq!(vec![0.5f64, 1f64, 1.5f64], vec![*a, *b, *c]); } + _ => unreachable!(), }, - FreeVec::Pure(_) => unreachable!() + FreeVec::Pure(_) => unreachable!(), } } //just to appease clippy without disabling the lint.... macro_rules! assert_nearly_equal { ($a:expr, $b:expr, $c:expr) => { - assert!((($a)-($b)).abs() < $c) + assert!((($a) - ($b)).abs() < $c) }; } #[test] - fn test_bind_no_lifetime(){ - let f = FreeVec::lift_f(vec![1,2]); - let f = f.bind(|x| if x % 2 == 0 { FreeVec::lift_f(vec![f64::from(x),f64::from(x) + 1.0f64])} else { FreeVec::Pure(f64::from(x))}); + fn test_bind_no_lifetime() { + let f = FreeVec::lift_f(vec![1, 2]); + let f = f.bind(|x| { + if x % 2 == 0 { + FreeVec::lift_f(vec![f64::from(x), f64::from(x) + 1.0f64]) + } else { + FreeVec::Pure(f64::from(x)) + } + }); match f { - FreeVec::Free(f) => { - match &**f { - [FreeVec::Pure(a),FreeVec::Free(b)] => { - - assert_nearly_equal!(*a, 1.0f64, f64::EPSILON); - match &***b { - [FreeVec::Pure(a), FreeVec::Pure(b)] => { - assert_nearly_equal!(*a, 2.0f64, f64::EPSILON); - assert_nearly_equal!(*b, 3.0f64, f64::EPSILON); - }, - _ => unreachable!() + FreeVec::Free(f) => match &**f { + [FreeVec::Pure(a), FreeVec::Free(b)] => { + assert_nearly_equal!(*a, 1.0f64, f64::EPSILON); + match &***b { + [FreeVec::Pure(a), FreeVec::Pure(b)] => { + assert_nearly_equal!(*a, 2.0f64, f64::EPSILON); + assert_nearly_equal!(*b, 3.0f64, f64::EPSILON); } - }, - _ => unreachable!() + _ => unreachable!(), + } } + _ => unreachable!(), }, - FreeVec::Pure(_) => unreachable!() + FreeVec::Pure(_) => unreachable!(), } } #[test] - fn test_apply_no_lifetime(){ - let functions = FreeVec::Free(Box::new(vec![FreeVec::Free(Box::new(vec![FreeVec::Pure((|x| i64::from(x)*2) as fn(u32) -> i64), FreeVec::Pure((|x| i64::from(x)+2) as fn(u32)->i64)])), FreeVec::Pure((|x| i64::from(x)-5) as fn(u32)->i64)])); - let free_monad = FreeVec::Free(Box::new(vec![FreeVec::Pure(5u32), FreeVec::Free(Box::new(vec![FreeVec::Pure(6u32), FreeVec::Pure(7u32)]))])); + fn test_apply_no_lifetime() { + let functions = FreeVec::Free(Box::new(vec![ + FreeVec::Free(Box::new(vec![ + FreeVec::Pure((|x| i64::from(x) * 2) as fn(u32) -> i64), + FreeVec::Pure((|x| i64::from(x) + 2) as fn(u32) -> i64), + ])), + FreeVec::Pure((|x| i64::from(x) - 5) as fn(u32) -> i64), + ])); + let free_monad = FreeVec::Free(Box::new(vec![ + FreeVec::Pure(5u32), + FreeVec::Free(Box::new(vec![FreeVec::Pure(6u32), FreeVec::Pure(7u32)])), + ])); let free_monad = free_monad.apply(functions.fmap(Into::into)); //what have I gotten myself into... //at least the mapped sub-trees are all identical in shape to m, so, they can be tested by the same test function... - let check_mlike_structure = |free_monad : &FreeVec<_>, pure1,pure2,pure3| { - match free_monad { - FreeVec::Free(vector) => { - match &***vector{ - [FreeVec::Pure(left),FreeVec::Free(right)] => { - assert_eq!(*left,pure1); - match &***right{ - [FreeVec::Pure(left), FreeVec::Pure(right)] => { - assert_eq!(*left, pure2); - assert_eq!(*right, pure3); - }, - _ => unreachable!() - } - }, - _ => unreachable!() + let check_mlike_structure = |free_monad: &FreeVec<_>, pure1, pure2, pure3| match free_monad + { + FreeVec::Free(vector) => match &***vector { + [FreeVec::Pure(left), FreeVec::Free(right)] => { + assert_eq!(*left, pure1); + match &***right { + [FreeVec::Pure(left), FreeVec::Pure(right)] => { + assert_eq!(*left, pure2); + assert_eq!(*right, pure3); + } + _ => unreachable!(), } - }, - FreeVec::Pure(_) => unreachable!() - } + } + _ => unreachable!(), + }, + FreeVec::Pure(_) => unreachable!(), }; //now, where are those sub-trees exactly, in this monstrosity? match free_monad { - FreeVec::Free(vector) => { - match &**vector{ - [FreeVec::Free(left), right] => { - match &***left { - [a,b] => { - check_mlike_structure(a, 10i64,12i64,14i64); - check_mlike_structure(b, 7i64,8i64,9i64); - }, - _ => unreachable!() + FreeVec::Free(vector) => match &**vector { + [FreeVec::Free(left), right] => { + match &***left { + [a, b] => { + check_mlike_structure(a, 10i64, 12i64, 14i64); + check_mlike_structure(b, 7i64, 8i64, 9i64); } - check_mlike_structure(right, 0i64,1i64,2i64); - }, - _ => unreachable!() + _ => unreachable!(), + } + check_mlike_structure(right, 0i64, 1i64, 2i64); } + _ => unreachable!(), }, - FreeVec::Pure(_) => unreachable!() + FreeVec::Pure(_) => unreachable!(), } } @@ -493,42 +496,57 @@ mod free_monad_tests{ use std::rc::Rc; #[derive(Clone)] - struct Conti<'a,A,B>(RcA + 'a>, RcA + 'a>); //two fields, to make apply testable. - impl<'a,A : 'a,B : 'a> Functor<'a,A> for Conti<'a,A,B>{ - type Target = Conti<'a,T,B>; - - fn fmap(self, f: F) -> Self::Target where F: Fn(A) -> C + 'a { + struct Conti<'a, A, B>(Rc A + 'a>, Rc A + 'a>); //two fields, to make apply testable. + impl<'a, A: 'a, B: 'a> Functor<'a, A> for Conti<'a, A, B> { + type Target = Conti<'a, T, B>; + + fn fmap(self, f: F) -> Self::Target + where + F: Fn(A) -> C + 'a, + { let f = Rc::new(f); let g = f.clone(); - Conti(Rc::new(move |x| f((self.0)(x))), Rc::new(move |x| g((self.1)(x)))) + Conti( + Rc::new(move |x| f((self.0)(x))), + Rc::new(move |x| g((self.1)(x))), + ) } } //need Bind and Pure to test retract. This is dumb, but it should fulfill the monad laws: - impl<'a, A : 'a, B : Clone+'a> Bind<'a, A> for Conti<'a,A,B>{ - type Target = Conti<'a,T,B>; + impl<'a, A: 'a, B: Clone + 'a> Bind<'a, A> for Conti<'a, A, B> { + type Target = Conti<'a, T, B>; - fn bind(self, f: F) -> Self::Target where F: Fn(A) -> Self::Target + 'a { + fn bind(self, f: F) -> Self::Target + where + F: Fn(A) -> Self::Target + '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))) + Conti( + Rc::new(move |x| (l(x.clone())).0(x)), + Rc::new(move |x| (r(x.clone())).1(x)), + ) } } - impl<'a, A : Clone+'a, B : 'a> Pure for Conti<'a,A,B>{ + impl<'a, A: Clone + 'a, B: 'a> Pure for Conti<'a, A, B> { fn pure(value: A) -> Self { let v2 = value.clone(); - Conti(Rc::new(move |_| value.clone()), Rc::new(move |_| v2.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 binder = |x : u32| { + fn test_conti_monad_laws() { + let binder = |x: u32| { let y = x; - Conti::(Rc::new(move |a| x + a*2), Rc::new(move |a| y * a+3)) + Conti::(Rc::new(move |a| x + a * 2), Rc::new(move |a| y * a + 3)) }; let test1 = Conti::pure(7u32); let v1 = test1.bind(binder); @@ -536,18 +554,15 @@ mod free_monad_tests{ assert_eq!((v1.0)(13), (v2.0)(13)); assert_eq!((v1.1)(17), (v2.1)(17)); - let test2 = Conti(Rc::new(|a| 31 + a*5), Rc::new(|b| 32*b+3)); - let bound_with_pure =test2.clone().bind(Conti::pure); + let test2 = Conti(Rc::new(|a| 31 + a * 5), Rc::new(|b| 32 * b + 3)); + let bound_with_pure = test2.clone().bind(Conti::pure); assert_eq!((test2.0)(3), (bound_with_pure.0)(3)); assert_eq!((test2.1)(5), (bound_with_pure.1)(5)); - let test3 = Conti(Rc::new(|a| 32 + (a*2)), Rc::new(|b| 32*b+7)); - let g = |x : u32| { - Conti::(Rc::new(move |a| x*2 + a), Rc::new(move |a| x * a+7)) - }; - let h = |x : u32| { - Conti::(Rc::new(move |a| x + a), Rc::new(move |a| x * a+12)) - }; + let test3 = Conti(Rc::new(|a| 32 + (a * 2)), Rc::new(|b| 32 * b + 7)); + let g = + |x: u32| Conti::(Rc::new(move |a| x * 2 + a), Rc::new(move |a| x * a + 7)); + let h = |x: u32| Conti::(Rc::new(move |a| x + a), Rc::new(move |a| x * a + 12)); let v1 = (test3.clone().bind(g)).bind(h); let v2 = test3.bind(|a| (g(a).bind(h))); @@ -557,64 +572,72 @@ mod free_monad_tests{ //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))); + 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){ + match (m.0)(4) { FreeConti::Pure(v) => assert_eq!(v, 8), - FreeConti::Free(_) => unreachable!() + FreeConti::Free(_) => unreachable!(), } - match (m.1)(4){ + match (m.1)(4) { FreeConti::Pure(v) => assert_eq!(v, 9), - FreeConti::Free(_) => unreachable!() + FreeConti::Free(_) => unreachable!(), } - }, - FreeConti::Pure(_) => unreachable!() + } + FreeConti::Pure(_) => 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))); + 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 functor = Conti(Rc::new(|x : u32| i32::try_from(x).unwrap()*3+2), Rc::new(|x| (i32::try_from(x).unwrap()+2)*5)); + fn test_fmap_lifetime() { + let functor = Conti( + Rc::new(|x: u32| i32::try_from(x).unwrap() * 3 + 2), + Rc::new(|x| (i32::try_from(x).unwrap() + 2) * 5), + ); let free_monad = FreeConti::lift_f(functor); - let free_monad = free_monad.fmap(|x : i32| f64::from(x)*0.25f64); + let free_monad = free_monad.fmap(|x: i32| f64::from(x) * 0.25f64); match free_monad { FreeConti::Free(f) => { let left = (f.0)(7); match left { FreeConti::Pure(v) => { assert_nearly_equal!(v, 5.75f64, f64::EPSILON); - }, - FreeConti::Free(_) => unreachable!() + } + FreeConti::Free(_) => unreachable!(), } let right = (f.1)(7); match right { FreeConti::Pure(v) => { assert_nearly_equal!(v, 11.25f64, f64::EPSILON); - }, - FreeConti::Free(_) => unreachable!() + } + FreeConti::Free(_) => unreachable!(), } - }, - FreeConti::Pure(_) => unreachable!() + } + FreeConti::Pure(_) => unreachable!(), } } #[test] - fn test_pure_lifetime(){ - let f : FreeConti<_,()> = FreeConti::pure(27); + fn test_pure_lifetime() { + let f: FreeConti<_, ()> = FreeConti::pure(27); match f { FreeConti::Pure(v) => assert_eq!(v, 27), FreeConti::Free(_) => unreachable!(), @@ -622,12 +645,18 @@ mod free_monad_tests{ } #[test] - fn test_bind_lifetime(){ - let functor = Conti(Rc::new(|x : u32| i32::try_from(x).unwrap()*3+2), Rc::new(|x| (i32::try_from(x).unwrap()+2)*5)); + fn test_bind_lifetime() { + let functor = Conti( + Rc::new(|x: u32| i32::try_from(x).unwrap() * 3 + 2), + Rc::new(|x| (i32::try_from(x).unwrap() + 2) * 5), + ); let free_monad = FreeConti::lift_f(functor); let free_monad = free_monad.bind(|y| { let z = y; - FreeConti::lift_f(Conti(Rc::new(move |x| f64::from(x)*0.25f64 + f64::from(y)), Rc::new(move |x| f64::from(x) * 0.5f64 - f64::from(z)))) + FreeConti::lift_f(Conti( + Rc::new(move |x| f64::from(x) * 0.25f64 + f64::from(y)), + Rc::new(move |x| f64::from(x) * 0.5f64 - f64::from(z)), + )) }); match free_monad { FreeConti::Free(f) => { @@ -641,11 +670,11 @@ mod free_monad_tests{ FreeConti::Free(_) => unreachable!(), } let right = (f.1)(5); - match right{ + match right { FreeConti::Pure(v) => assert_nearly_equal!(v, -11.5f64, f64::EPSILON), FreeConti::Free(_) => unreachable!(), } - }, + } FreeConti::Pure(_) => unreachable!(), } let right = (f.1)(4); @@ -662,26 +691,30 @@ mod free_monad_tests{ FreeConti::Pure(v) => assert_nearly_equal!(v, -27.5f64, f64::EPSILON), FreeConti::Free(_) => unreachable!(), } - }, + } FreeConti::Pure(_) => unreachable!(), } - }, - FreeConti::Pure(_) => unreachable!() + } + FreeConti::Pure(_) => unreachable!(), } } #[test] - fn test_apply_lifetime(){ + fn test_apply_lifetime() { //oh, god, please no. - let free_monad_input = FreeConti::lift_f(Conti(Rc::new(|x : u32| i32::try_from(x).unwrap()*3+2), Rc::new(|x| (i32::try_from(x).unwrap()+2)*5))); - let functions = FreeConti::lift_f(Conti(Rc::new(|x : u32| -> ApplyFn { - (move |y : i32| f64::from(y + i32::try_from(x).unwrap())).into() - }), Rc::new(|x : u32| { - (move |y : i32| f64::from(y*i32::try_from(x).unwrap())).into() - }))); + let free_monad_input = FreeConti::lift_f(Conti( + Rc::new(|x: u32| i32::try_from(x).unwrap() * 3 + 2), + Rc::new(|x| (i32::try_from(x).unwrap() + 2) * 5), + )); + let functions = FreeConti::lift_f(Conti( + Rc::new(|x: u32| -> ApplyFn { + (move |y: i32| f64::from(y + i32::try_from(x).unwrap())).into() + }), + Rc::new(|x: u32| (move |y: i32| f64::from(y * i32::try_from(x).unwrap())).into()), + )); //make it stop! let free_monad = free_monad_input.apply(functions); - + match free_monad { FreeConti::Free(m) => { let left = (m.0)(5u32); @@ -689,15 +722,15 @@ mod free_monad_tests{ FreeConti::Free(m) => { let left = (m.0)(7u32); match left { - FreeConti::Pure(v) => assert_nearly_equal!(v,28f64,f64::EPSILON), + FreeConti::Pure(v) => assert_nearly_equal!(v, 28f64, f64::EPSILON), FreeConti::Free(_) => unreachable!(), } let right = (m.1)(7u32); match right { - FreeConti::Pure(v) => assert_nearly_equal!(v,50f64, f64::EPSILON), + FreeConti::Pure(v) => assert_nearly_equal!(v, 50f64, f64::EPSILON), FreeConti::Free(_) => unreachable!(), } - }, + } FreeConti::Pure(_) => unreachable!(), } let right = (m.1)(5u32); @@ -705,21 +738,24 @@ mod free_monad_tests{ FreeConti::Free(m) => { let left = (m.0)(7u32); match left { - FreeConti::Pure(v) => assert_nearly_equal!(v,5f64*23f64, f64::EPSILON), + FreeConti::Pure(v) => { + assert_nearly_equal!(v, 5f64 * 23f64, f64::EPSILON) + } FreeConti::Free(_) => unreachable!(), } let right = (m.1)(7u32); match right { - FreeConti::Pure(v) => assert_nearly_equal!(v, 5f64*45f64, f64::EPSILON), + FreeConti::Pure(v) => { + assert_nearly_equal!(v, 5f64 * 45f64, f64::EPSILON) + } FreeConti::Free(_) => unreachable!(), } - }, + } FreeConti::Pure(_) => unreachable!(), } - }, - FreeConti::Pure(_) => unreachable!() + } + FreeConti::Pure(_) => unreachable!(), } //let's never speak of this again. } - -} \ No newline at end of file +} -- cgit v1.2.3