diff --git a/src/func/test_suite.rs b/src/func/test_suite.rs index 5ec0f3a..751269b 100644 --- a/src/func/test_suite.rs +++ b/src/func/test_suite.rs @@ -88,5 +88,10 @@ pub fn monad_follows_laws<'a, T: Monad<'a> + FunctorTestSuite<'a>>() -> R { res += join_can_be_expressed_via_bind::(|| pa(pb(2))); }) }); + T::sample(|pa| { + T::sample(|pb| { + res += iterate_can_be_expressed_via_bind::(|| pa(2), || |x| pb(x + 3)); + }) + }); res } diff --git a/src/func/tests.rs b/src/func/tests.rs index 918625c..c41d6da 100644 --- a/src/func/tests.rs +++ b/src/func/tests.rs @@ -3,6 +3,8 @@ use std::{ ops::{Add, AddAssign}, }; +use controlflow::IterativeWrapped; + use super::*; pub struct TestResults { @@ -361,3 +363,40 @@ pub fn join_can_be_expressed_via_bind< T::bind(ffa0(), |fa| fa), ) } + +enum TestIterative<'a, T: Monad<'a>, A: 'a + Send, B: 'a + Send, F: 'a + FnOnce(A) -> T::F> { + Pre(T::F, F), + Post(T::F), +} + +impl<'a, T: Monad<'a>, A: 'a + Send, B: 'a + Send, F: 'a + Send + FnOnce(A) -> T::F> + Iterative<'a> for TestIterative<'a, T, A, B, F> +{ + type B = B; + + type T = T; + + fn next(self) -> IterativeWrapped<'a, Self> { + match self { + Self::Pre(fa, f) => T::fmap(fa, |a| ControlFlow::Continue(Self::Post(f(a)))), + Self::Post(fb) => T::fmap(fb, ControlFlow::Break), + } + } +} + +pub fn iterate_can_be_expressed_via_bind< + 'a, + T: Monad<'a> + Eqr<'a>, + A: 'a + Send, + B: 'a + Send + Debug + PartialEq, + F: 'a + Send + Fn(A) -> T::F, +>( + fa0: impl 'a + Fn() -> T::F, + f0: impl 'a + Fn() -> F, +) -> R { + T::eqr( + "iterate via bind", + T::iterate(TestIterative::Pre(fa0(), f0())), + T::bind(fa0(), f0()), + ) +}