diff --git a/src/func.rs b/src/func.rs index a828fc6..47ea22f 100644 --- a/src/func.rs +++ b/src/func.rs @@ -21,9 +21,10 @@ pub use std::ops::ControlFlow; pub use radn_derive::{CovariantFunctor, SharedFunctor}; -use self::controlflow::*; +use self::controlflow::{BindableMut, ControlFlowClass}; +pub use self::controlflow::{Iterative, IterativeWrapped}; -/// Part of Haskell's `Functor f` responsible to use `f a`. +/// Part of Haskell's `Functor f` responsible for having `f a`. /// /// pub trait WeakFunctor { @@ -172,14 +173,30 @@ pub trait Monad: Applicative { where Self: 'a; + /// Simplified, [`FnMut`] version of [`Monad::iibind`]. + /// Reasoning for this method existing at all is that + /// most usecases are better modelled + /// + /// Note: hypothetically, you can implement [`Monad::iibind`] from [`Monad::ibind`], + /// but it's highly discouraged. + fn ibind<'a, A: 'a, B: 'a>( + a: A, + f: impl 'a + FnMut(A) -> Self::F<'a, ControlFlow>, + ) -> Self::F<'a, B> + where + Self: 'a, + { + Self::iibind(a, BindableMut::new(f)) + } + /// Kleisli category composition special case used to represent loops. /// Included for optimisation and clarity. /// Generally, [`Monad::bind`] should be enough implement it. /// See [`classes::stackless::StacklessClass::ibind`] for a generic, though less-than ideal, blanket implementation. - /// On practice, you shouldn't be using [`Monad::bind`]/[`Pure::pure`]/[`Functor::fmap`] here. - fn ibind<'a, A: 'a, B: 'a>( + /// On practice, you generally shouldn't be using [`Monad::bind`]/[`Pure::pure`]/[`Functor::fmap`] here. + fn iibind<'a, A: 'a, B: 'a>( a: A, - f: impl 'a + FnMut(A) -> Self::F<'a, ControlFlow>, + f: impl Iterative<'a, T = Self, A = A, B = B>, ) -> Self::F<'a, B> where Self: 'a; diff --git a/src/func/classes/composition.rs b/src/func/classes/composition.rs index 7261f69..00a26d0 100644 --- a/src/func/classes/composition.rs +++ b/src/func/classes/composition.rs @@ -107,11 +107,21 @@ impl Monad for CompositionClass { Self: 'a, { U::ibind(a, move |a| { - let fx = f(a); - U::fmap(|ustate| V::unstuff(ustate), fx) + let fstate = f(a); + U::fmap(|ustate| V::unstuff(ustate), fstate) }) } + fn iibind<'a, A: 'a, B: 'a>( + a: A, + f: impl Iterative<'a, T = Self, A = A, B = B>, + ) -> Self::F<'a, B> + where + Self: 'a, + { + U::iibind(a, ComposedBindable(f)) + } + fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> where Self::F<'a, A>: 'a, @@ -121,6 +131,33 @@ impl Monad for CompositionClass { } } +struct ComposedBindable(F); + +impl< + 'a, + U: 'a + Monad, + V: 'a + Monad + LocalFunctor, + F: Iterative<'a, T = CompositionClass>, + > Iterative<'a> for ComposedBindable +{ + type A = F::A; + type B = ::F<'a, F::B>; + type T = U; + + fn iterate(self, a: Self::A) -> IterativeWrapped<'a, Self> { + let fstate = self.0.iterate(a); + U::fmap( + |ustate| match V::unstuff(ustate) { + ControlFlow::Continue((next_a, next_f)) => { + ControlFlow::Continue((next_a, ComposedBindable(next_f))) + } + ControlFlow::Break(b) => ControlFlow::Break(b), + }, + fstate, + ) + } +} + impl + LocalFunctor> MonadFail for CompositionClass { fn fail<'a, A: 'a>(e: E) -> Self::F<'a, A> where diff --git a/src/func/classes/future.rs b/src/func/classes/future.rs index dbf3e9f..d33baa8 100644 --- a/src/func/classes/future.rs +++ b/src/func/classes/future.rs @@ -104,6 +104,23 @@ impl Monad for FutureClass { }) } + fn iibind<'a, A: 'a, B: 'a>( + mut a: A, + mut f: impl Iterative<'a, T = Self, A = A, B = B>, + ) -> Self::F<'a, B> + where + Self: 'a, + { + Box::pin(async move { + loop { + match f.iterate(a).await { + ControlFlow::Continue((next_a, next_f)) => (a, f) = (next_a, next_f), + ControlFlow::Break(b) => return b, + } + } + }) + } + fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> { Box::pin(async { ffa.await.await }) } diff --git a/src/func/classes/lazy.rs b/src/func/classes/lazy.rs index 8128dbf..83700d1 100644 --- a/src/func/classes/lazy.rs +++ b/src/func/classes/lazy.rs @@ -103,6 +103,21 @@ impl Monad for LazyClass { }) } + fn iibind<'a, A: 'a, B: 'a>( + mut a: A, + mut f: impl Iterative<'a, T = Self, A = A, B = B>, + ) -> Self::F<'a, B> + where + Self: 'a, + { + loop { + match f.iterate(a)() { + ControlFlow::Continue((next_a, next_f)) => (a, f) = (next_a, next_f), + ControlFlow::Break(b) => return Self::pure(b), + } + } + } + fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> { Box::new(|| ffa()()) } diff --git a/src/func/classes/option.rs b/src/func/classes/option.rs index 8106158..232efd0 100644 --- a/src/func/classes/option.rs +++ b/src/func/classes/option.rs @@ -107,6 +107,21 @@ impl Monad for OptionClass { } } + fn iibind<'a, A: 'a, B: 'a>( + mut a: A, + mut f: impl Iterative<'a, T = Self, A = A, B = B>, + ) -> Self::F<'a, B> + where + Self: 'a, + { + loop { + match f.iterate(a)? { + ControlFlow::Continue((next_a, next_f)) => (a, f) = (next_a, next_f), + ControlFlow::Break(b) => return Self::pure(b), + } + } + } + fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> { ffa? } diff --git a/src/func/classes/result.rs b/src/func/classes/result.rs index 0649a60..7957831 100644 --- a/src/func/classes/result.rs +++ b/src/func/classes/result.rs @@ -128,6 +128,21 @@ impl Monad for ResultClass { } } + fn iibind<'a, A: 'a, B: 'a>( + mut a: A, + mut f: impl Iterative<'a, T = Self, A = A, B = B>, + ) -> Self::F<'a, B> + where + Self: 'a, + { + loop { + match f.iterate(a)? { + ControlFlow::Continue((next_a, next_f)) => (a, f) = (next_a, next_f), + ControlFlow::Break(b) => return Self::pure(b), + } + } + } + fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> where Self: 'a, diff --git a/src/func/classes/solo.rs b/src/func/classes/solo.rs index 94b7650..855ccea 100644 --- a/src/func/classes/solo.rs +++ b/src/func/classes/solo.rs @@ -95,6 +95,21 @@ impl Monad for SoloClass { } } + fn iibind<'a, A: 'a, B: 'a>( + mut a: A, + mut f: impl Iterative<'a, T = Self, A = A, B = B>, + ) -> Self::F<'a, B> + where + Self: 'a, + { + loop { + match f.iterate(a) { + ControlFlow::Continue((new_a, new_f)) => (a, f) = (new_a, new_f), + ControlFlow::Break(b) => return b, + } + } + } + fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> { ffa } diff --git a/src/func/classes/stackless.rs b/src/func/classes/stackless.rs index e9b0bf3..244a27b 100644 --- a/src/func/classes/stackless.rs +++ b/src/func/classes/stackless.rs @@ -233,7 +233,22 @@ impl Monad for StacklessClass { { Self::pure(a).bind(move |a| { f(a).bind(|state| match state { - ControlFlow::Continue(a) => Self::ibind(a, f), + ControlFlow::Continue(next_a) => Self::ibind(next_a, f), + ControlFlow::Break(b) => Self::pure(b), + }) + }) + } + + fn iibind<'a, A: 'a, B: 'a>( + a: A, + f: impl Iterative<'a, T = Self, A = A, B = B>, + ) -> Self::F<'a, B> + where + Self: 'a, + { + Self::pure(a).bind(move |a| { + f.iterate(a).bind(|state| match state { + ControlFlow::Continue((next_a, next_f)) => Self::iibind(next_a, next_f), ControlFlow::Break(b) => Self::pure(b), }) }) diff --git a/src/func/controlflow.rs b/src/func/controlflow.rs index 9f59dbb..807032b 100644 --- a/src/func/controlflow.rs +++ b/src/func/controlflow.rs @@ -1,3 +1,5 @@ +use std::marker::PhantomData; + use super::*; pub struct ControlFlowClass(ControlFlow<(), C>); @@ -28,3 +30,60 @@ impl Pure for ControlFlowClass { ControlFlow::Break(a) } } + +/// Next [Iterative] state, wrapped. +pub type IterativeWrapped<'a, G> = <>::T as WeakFunctor>::F< + 'a, + ControlFlow<>::B, (>::A, G)>, +>; + +/// Value passed to [`Monad::iibind`]. +pub trait Iterative<'a>: 'a + Sized { + /// [`ControlFlow::Continue`] + type A: 'a; + /// [`ControlFlow::Break`] + type B: 'a; + /// Corresponding [`WeakFunctor`]. + type T: 'a + ?Sized + WeakFunctor; + /// Get next state. + fn iterate(self, a: Self::A) -> IterativeWrapped<'a, Self>; +} + +pub struct BindableMut(F, PhantomData, PhantomData, PhantomData); + +impl< + 'a, + T: 'a + ?Sized + Functor, + A: 'a, + B: 'a, + F: 'a + FnMut(A) -> T::F<'a, ControlFlow>, + > BindableMut +{ + pub fn new(f: F) -> Self { + BindableMut(f, PhantomData, PhantomData, PhantomData) + } +} + +impl< + 'a, + T: 'a + ?Sized + Functor, + A: 'a, + B: 'a, + F: 'a + FnMut(A) -> T::F<'a, ControlFlow>, + > Iterative<'a> for BindableMut +{ + type A = A; + type B = B; + type T = T; + + fn iterate(mut self, a: Self::A) -> IterativeWrapped<'a, Self> { + let fa = self.0(a); + T::fmap( + move |state| match state { + ControlFlow::Continue(next_a) => ControlFlow::Continue((next_a, self)), + ControlFlow::Break(b) => ControlFlow::Break(b), + }, + fa, + ) + } +} diff --git a/src/std/tracing.rs b/src/std/tracing.rs index 01d6b7c..18a1f29 100644 --- a/src/std/tracing.rs +++ b/src/std/tracing.rs @@ -182,6 +182,24 @@ impl Monad for TracedClass { } } + fn iibind<'a, A: 'a, B: 'a>( + mut a: A, + mut f: impl Iterative<'a, T = Self, A = A, B = B>, + ) -> Self::F<'a, B> + where + Self: 'a, + { + let mut t = TraceBox::pure(); + loop { + let fa = f.iterate(a); + t = fa.t.after(t); + match fa.a { + ControlFlow::Continue((next_a, next_f)) => (a, f) = (next_a, next_f), + ControlFlow::Break(b) => return b.with_trace(t), + } + } + } + fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> where Self::F<'a, A>: 'a, diff --git a/src/testing/counted.rs b/src/testing/counted.rs index ff787b3..d7d0b5d 100644 --- a/src/testing/counted.rs +++ b/src/testing/counted.rs @@ -164,6 +164,24 @@ impl Monad for CountedClass { } } + fn iibind<'a, A: 'a, B: 'a>( + mut a: A, + mut f: impl Iterative<'a, T = Self, A = A, B = B>, + ) -> Self::F<'a, B> + where + Self: 'a, + { + let mut n = 0; + loop { + let fa = f.iterate(a); + n += fa.n; + match fa.a { + ControlFlow::Continue((next_a, next_f)) => (a, f) = (next_a, next_f), + ControlFlow::Break(b) => return b.with_count(n), + } + } + } + fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> where Self::F<'a, A>: 'a,