aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndreas Grois <andi@grois.info>2023-04-02 21:59:22 +0200
committerAndreas Grois <andi@grois.info>2023-04-02 21:59:22 +0200
commitcebdd3be32d50be379663e92d4428e6bba19ba51 (patch)
tree8c5b57f2e2ed3c089e0546fdb95ef8c2082d1952 /src
parent418f514fc46f45ae2901753e3398adb33664bed9 (diff)
Run cargo fmt
I think readability was better before that...
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs414
1 files changed, 225 insertions, 189 deletions
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<FreeMonadTypeName<'a, A>>)`,
//! 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<A,F> where F : Functor<Free<A,F>>`. If one now wants to implement the [`Functor`][higher::Functor]
//! trait for this, it is not really possible to express the `Target<T> = Free<A,F::Target<Free<A,F::Target<...>>>>` 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<A>`, the syntax would be `free!(FreeFunky<A>, Funky<FreeFunky<A>>)`.
/// This declares an enum named `FreeFunky<A>`, 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<A>, Option<FreeOption<A>>);
/// ```
-///
+///
/// 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<A>, Option<FreeOption<A>>);
-///
+///
/// 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<A,E>, Result<FreeResult<A,E>,E>);
-///
-/// fn returns_a_monad<'a, A, E>(r : Result<A,E>) -> impl Monad<'a,A>
+///
+/// fn returns_a_monad<'a, A, E>(r : Result<A,E>) -> 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<A>` example from above. In this case, the signatures are
/// `fn lift_f(functor : Option<A>) -> FreeOption<A>` and
/// `fn retract(self : FreeOption<A>) -> Option<A>`
-///
+///
/// `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<FreeMacroReservedType> = $name<$($other_lifetimes,)* FreeMacroReservedType $(,$other_generics)*>;
fn fmap<FreeMacroReservedType,FreeMacroReservedType2>(self, f: FreeMacroReservedType2) -> Self::Target<FreeMacroReservedType> 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<FreeMacroReservedType> = $name<$($other_lifetimes,)* FreeMacroReservedType $(,$other_generics)*>;
- fn fmap<FreeMacroReservedType,F>(self, f: F) -> Self::Target<FreeMacroReservedType>
+ fn fmap<FreeMacroReservedType,F>(self, f: F) -> Self::Target<FreeMacroReservedType>
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<F>) -> $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<A>, Vec<FreeVec<A>>);
#[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>(Rc<dyn Fn(B)->A + 'a>, Rc<dyn Fn(B)->A + 'a>); //two fields, to make apply testable.
- impl<'a,A : 'a,B : 'a> Functor<'a,A> for Conti<'a,A,B>{
- type Target<T> = Conti<'a,T,B>;
-
- fn fmap<C, F>(self, f: F) -> Self::Target<C> where F: Fn(A) -> C + 'a {
+ struct Conti<'a, A, B>(Rc<dyn Fn(B) -> A + 'a>, Rc<dyn Fn(B) -> A + 'a>); //two fields, to make apply testable.
+ impl<'a, A: 'a, B: 'a> Functor<'a, A> for Conti<'a, A, B> {
+ type Target<T> = Conti<'a, T, B>;
+
+ fn fmap<C, F>(self, f: F) -> Self::Target<C>
+ 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<T> = Conti<'a,T,B>;
+ impl<'a, A: 'a, B: Clone + 'a> Bind<'a, A> for Conti<'a, A, B> {
+ 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 {
+ 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)))
+ 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<A> for Conti<'a,A,B>{
+ impl<'a, A: Clone + 'a, B: 'a> Pure<A> 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::<u32,u32>(Rc::new(move |a| x + a*2), Rc::new(move |a| y * a+3))
+ Conti::<u32, u32>(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::<u32,u32>(Rc::new(move |a| x*2 + a), Rc::new(move |a| x * a+7))
- };
- let h = |x : u32| {
- Conti::<u32,u32>(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::<u32, u32>(Rc::new(move |a| x * 2 + a), Rc::new(move |a| x * a + 7));
+ let h = |x: u32| Conti::<u32, u32>(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<i32,f64> {
- (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<i32, f64> {
+ (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
+}