diff --git a/src/func.rs b/src/func.rs index dd0d4ef..bf91e58 100644 --- a/src/func.rs +++ b/src/func.rs @@ -1,3 +1,12 @@ +//! Generic functional concepts implemented in Rust. +//! Some choices are intentionally less generic due to specifics of the domain. +//! That, for example, includes the almost exclusive focus on [`FnOnce`] category. +//! +//! Sources: +//! * +//! * +//! * + pub mod classes; pub mod clone_func; pub mod copy_func; @@ -7,13 +16,16 @@ pub mod test_suite; #[cfg(test)] pub mod tests; +/// Part of Haskell's `Functor f` responsible to use `f a`. +/// +/// pub trait WeakFunctor { type F<'a, A: 'a>: 'a where Self: 'a; } -/// Rust-specific implementation of [Functor], respecting `move` semantics. +/// Rust-specific implementation of [`Functor`], respecting `move` semantics. /// /// Cannot insantiate for e.g. multi-element collections: /// ```compile_fail @@ -33,7 +45,7 @@ pub trait WeakFunctor { /// ``` /// Why does it fail to compile? `.map` expects `FnMut` (or we can think of it being `Fn`, doesn't matter here). But what we provide it (`f`) is /// -/// For Haskell-style Functors, use [clone_func::CloneFunctor] instead. +/// For Haskell-style Functors, use [`clone_func::CloneFunctor`] instead. /// ``` /// use radn_rs::func::clone_func::*; /// @@ -52,12 +64,17 @@ pub trait WeakFunctor { /// } /// } /// ``` - +/// +/// pub trait Functor: WeakFunctor { + /// Equivalent or Haskell's `fmap`. + /// Due to Rust limitations, it's not a `function->function` conversion. + /// For that see [`derivations::fmap`]. fn fmap<'a, A: 'a, B: 'a>(f: impl 'a + FnOnce(A) -> B, fa: Self::F<'a, A>) -> Self::F<'a, B> where Self: 'a; + /// Equivalent of Haskell's `$>`/`<$`. fn replace<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, b: B) -> Self::F<'a, B> where Self: 'a, @@ -65,6 +82,7 @@ pub trait Functor: WeakFunctor { Self::fmap(|_| b, fa) } + /// Equivalent of Haskell's `void`. fn void<'a, A: 'a>(fa: Self::F<'a, A>) -> Self::F<'a, ()> where Self: 'a, @@ -73,13 +91,9 @@ pub trait Functor: WeakFunctor { } } -pub fn fmap<'a, T: 'a + Functor, A: 'a, B: 'a>( - f: impl 'a + FnOnce(A) -> B, -) -> impl FnOnce(T::F<'a, A>) -> T::F<'a, B> { - move |fa| T::fmap(f, fa) -} - +/// Part of [`Applicative`] responsible for Haskell's sequential application `<*>`. pub trait ApplicativeSeq: Functor { + /// Equivalent of Haskell's `<*>`. fn seq<'a, A: 'a, B: 'a>( ff: Self::F<'a, impl 'a + FnOnce(A) -> B>, fa: Self::F<'a, A>, @@ -88,7 +102,9 @@ pub trait ApplicativeSeq: Functor { Self: 'a; } +/// Part of [`Applicative`] responsible for Haskell's result combination `listA2`. pub trait ApplicativeLA2: Functor { + /// Equivalent of Haskell's `listA2`. fn la2<'a, A: 'a, B: 'a, C: 'a>( f: impl 'a + FnOnce(A, B) -> C, fa: Self::F<'a, A>, @@ -98,15 +114,23 @@ pub trait ApplicativeLA2: Functor { Self: 'a; } +/// Part of [`Applicative`] responsible for Rust-style result combination, specifically for tuples. pub trait ApplicativeTuple: Functor { + /// Similar to Haskell's `listA2` but with [Iterator::collect]-ish semantics. fn tuple<'a, A: 'a, B: 'a>(fab: (Self::F<'a, A>, Self::F<'a, B>)) -> Self::F<'a, (A, B)> where Self: 'a; } +/// Equivalent of Haskell's `Applicative`. +/// Split into [`ApplicativeSeq`], [`ApplicativeLA2`] and [`ApplicativeTuple`] due to Rust limitations. +/// +/// pub trait Applicative: Functor + ApplicativeSeq + ApplicativeLA2 + ApplicativeTuple { + /// Equivalent of Haskell's `pure`/`return`. fn pure<'a, A: 'a>(a: A) -> Self::F<'a, A>; + /// Equivalent of Haskell's `*>`/`>>`. fn discard_first<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, fb: Self::F<'a, B>) -> Self::F<'a, B> where Self: 'a, @@ -114,6 +138,7 @@ pub trait Applicative: Functor + ApplicativeSeq + ApplicativeLA2 + ApplicativeTu Self::seq(Self::replace(fa, |b| b), fb) } + /// Equivalent of Haskell's `<*`. fn discard_second<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, fb: Self::F<'a, B>) -> Self::F<'a, A> where Self: 'a, @@ -122,7 +147,11 @@ pub trait Applicative: Functor + ApplicativeSeq + ApplicativeLA2 + ApplicativeTu } } +/// Equivalent of Haskell's `Monad`. +/// +/// pub trait Monad: Applicative { + /// Equivalent of Haskell's `>==`. fn bind<'a, A: 'a, B: 'a>( fa: Self::F<'a, A>, f: impl 'a + FnOnce(A) -> Self::F<'a, B>, @@ -130,6 +159,7 @@ pub trait Monad: Applicative { where Self: 'a; + /// Equivalent of Haskell's `join`. fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> where Self::F<'a, A>: 'a, @@ -139,12 +169,28 @@ pub trait Monad: Applicative { } } +/// Equivalent of Haskell's `MonadFail`. +/// +/// pub trait MonadFail: Monad { - fn fail<'a, A>(e: E) -> Self::F<'a, A>; + /// Equivalent of Haskell's `fail`. + fn fail<'a, A: 'a>(e: E) -> Self::F<'a, A> + where + Self: 'a; } +/// Equivalent of Haskell's `Alternative`. +/// Lacks `some`/`many` definitions due to [`FnOnce`] category semantics. +/// +/// pub trait Alternative: Applicative { - fn empty<'a, A>() -> Self::F<'a, A>; + /// Equivalent of Haskell's `empty`. + fn empty<'a, A: 'a>() -> Self::F<'a, A> + where + Self: 'a; - fn add<'a, A>(fa: Self::F<'a, A>, fb: Self::F<'a, A>) -> Self::F<'a, A>; + /// Equivalent of Haskell's `<|>`. + fn add<'a, A: 'a>(fa: Self::F<'a, A>, fb: Self::F<'a, A>) -> Self::F<'a, A> + where + Self: 'a; } diff --git a/src/func/classes.rs b/src/func/classes.rs index cee538c..9d1c030 100644 --- a/src/func/classes.rs +++ b/src/func/classes.rs @@ -1,3 +1,24 @@ +//! Implementation of some basic classes (in Haskell's meaning of a class). +//! +//! To get an understanding of what classes are about, see [`option`]. +//! +//! For [`MonadFail`] examples, see [`result`][^research]. +//! +//! For the simplest form (even ChatGPT can understand it! lol) of [`Monad`], see [`solo`][^production]. +//! +//! For async support, see [`future`][^production][^research]. +//! +//! For "creative" execution models, see [`lazy`][^research] and [`stackless`][^research]. +//! +//! To see when not to use [Monad]s, see [`composition`][^research]. +//! +//! [^production]: classes expected to be used in production. +//! +//! [^research]: classes used for research purposes to enhance the abstract interfaces. + +#[cfg(doc)] +use crate::func::*; + pub mod composition; pub mod future; pub mod lazy; diff --git a/src/func/classes/future.rs b/src/func/classes/future.rs index 53866ca..bb66f8a 100644 --- a/src/func/classes/future.rs +++ b/src/func/classes/future.rs @@ -1,3 +1,8 @@ +//! Async [Monad] based on [`Pin>`] (see: [`Pin`], [`Box::pin`], [`Future`]). +//! This generally allows just using `.await` on wrapped instances. +//! +//! For sync, see [`super::solo`]. + use std::{future::Future, pin::Pin}; use futures::join; diff --git a/src/func/classes/lazy.rs b/src/func/classes/lazy.rs index ccd927c..8990a4c 100644 --- a/src/func/classes/lazy.rs +++ b/src/func/classes/lazy.rs @@ -1,6 +1,15 @@ +//! Helper [Monad]s to defer execution until necessary. +//! Wrapped value is just a box pointing to the constructor function. +//! +//! Due to semantical laziness, +//! [`LazyClass::replace`] and [`LazyClass::discard_first`]/[`LazyClass::discard_second`] +//! actually fully cancel the "unnecessary" computation. +//! +//! For stackless execution see [`super::stackless`]. + use crate::func::*; -struct LazyClass; +pub struct LazyClass; impl WeakFunctor for LazyClass { type F<'a, A: 'a> = Box A>; diff --git a/src/func/classes/option.rs b/src/func/classes/option.rs index 50d0d22..cb72511 100644 --- a/src/func/classes/option.rs +++ b/src/func/classes/option.rs @@ -1,3 +1,12 @@ +//! Implementation of [`Monad`] for [`Option`]. +//! +//! If any of the input values are [`None`], you can expect the output to be [`None`] as well. +//! That includes +//! [`OptionClass::replace`] and [`OptionClass::discard_first`]/[`OptionClass::discard_second`], +//! even if the value of the option would be ignored. +//! +//! For [`Result`] alternative see [`super::result`] + use crate::func::*; pub struct OptionClass; diff --git a/src/func/classes/result.rs b/src/func/classes/result.rs index bb5f21f..3c6375e 100644 --- a/src/func/classes/result.rs +++ b/src/func/classes/result.rs @@ -1,3 +1,12 @@ +//! Implementation of [`MonadFail`] for [`Result`]. +//! +//! If any of the input values are [`Err`], you can expect the output to be [`Err`] as well. +//! That includes +//! [`ResultClass::replace`] and [`ResultClass::discard_first`]/[`ResultClass::discard_second`], +//! even if the value of the option would be ignored. +//! +//! For [`Option`] alternative see [`super::option`] + use crate::func::*; pub struct ResultClass(E); @@ -72,3 +81,12 @@ impl Monad for ResultClass { ffa? } } + +impl MonadFail for ResultClass { + fn fail<'a, A: 'a>(e: E) -> Self::F<'a, A> + where + Self: 'a, + { + Err(e) + } +} diff --git a/src/func/classes/solo.rs b/src/func/classes/solo.rs index 12c0a39..6140290 100644 --- a/src/func/classes/solo.rs +++ b/src/func/classes/solo.rs @@ -1,3 +1,7 @@ +//! Simplest sync [Monad]. +//! +//! For async, see [`super::future`]. + use crate::func::*; pub struct SoloClass; diff --git a/src/func/classes/stackless.rs b/src/func/classes/stackless.rs index f7a96ae..3b36eac 100644 --- a/src/func/classes/stackless.rs +++ b/src/func/classes/stackless.rs @@ -1,3 +1,8 @@ +//! Helper [Monad]s to move deep execution chains off the stack onto the heap. +//! [`Stackless`] represents a wrapped value. +//! +//! For lazy stackful execution see [`super::lazy`]. + use std::{cell::Cell, rc::Rc}; use crate::func::derivations::*; @@ -50,7 +55,9 @@ impl<'a, A: 'a> Stackless<'a, A> { self.0(Box::new(f)) } - fn bind(self, f: impl 'a + FnOnce(A) -> Stackless<'a, B>) -> Stackless<'a, B> { + /// Method-like equivalent of [`Monad::bind`], + /// the preferred way to chain [`Stackless`] and `FnOnce(A) -> Stackless` into [`Stackless`]. + pub fn bind(self, f: impl 'a + FnOnce(A) -> Stackless<'a, B>) -> Stackless<'a, B> { Stackless(Box::new(|takesb| { let lcell = Rc::new(Cell::new(None)); let rcell = lcell.clone(); @@ -66,7 +73,8 @@ impl<'a, A: 'a> Stackless<'a, A> { })) } - fn map(self, f: impl 'a + FnOnce(A) -> B) -> Stackless<'a, B> { + /// Method-like equivalent of [`Functor::fmap`]. + pub fn map(self, f: impl 'a + FnOnce(A) -> B) -> Stackless<'a, B> { Stackless(Box::new(|takesb| { let lcell = Rc::new(Cell::new(None)); let rcell = lcell.clone(); @@ -85,6 +93,8 @@ impl<'a, A: 'a> Stackless<'a, A> { })) } + /// Evaluate. Process is loop-like on the inside + /// with the least amount of recursion the current model allows to use. pub fn evaluate(self) -> A { let ocell = Rc::new(Cell::new(None)); let icell = ocell.clone(); diff --git a/src/func/clone_func.rs b/src/func/clone_func.rs index 56a374f..5eb5199 100644 --- a/src/func/clone_func.rs +++ b/src/func/clone_func.rs @@ -1,3 +1,9 @@ +//! Equivalent of [`crate::func`] for [`Clone`]/[`Fn`] Category. +//! Similar to what you'd encounter in Haskell. +//! Not well maintained due to not being used in the main RADN code base. +//! +//! See also: [`super::copy_func`] + pub trait CloneWeakFunctor { type ClF<'a, A: Clone>: Clone; } diff --git a/src/func/copy_func.rs b/src/func/copy_func.rs index a65c10f..fd4ebc3 100644 --- a/src/func/copy_func.rs +++ b/src/func/copy_func.rs @@ -1,3 +1,8 @@ +//! Equivalent of [`crate::func`] for [`Copy`]/[`Fn`] Category. +//! Not well maintained due to not being used in the main RADN code base. +//! +//! See also: [`super::clone_func`] + pub trait CopyWeakFunctor { type CF<'a, A: Copy>: Copy; } diff --git a/src/func/derivations.rs b/src/func/derivations.rs index 461e04a..6a2a698 100644 --- a/src/func/derivations.rs +++ b/src/func/derivations.rs @@ -1,5 +1,14 @@ +//! Useful helper functions/methods to extrapolate the existing behaviour. + use crate::func::*; +/// Equivalent of Haskell's `fmap`. `function-function` equivalent of [Functor::fmap]. +pub fn fmap<'a, T: 'a + Functor, A: 'a, B: 'a>( + f: impl 'a + FnOnce(A) -> B, +) -> impl FnOnce(T::F<'a, A>) -> T::F<'a, B> { + move |fa| T::fmap(f, fa) +} + pub trait ApplicativeLA2ViaSeq: ApplicativeSeq { fn _la2_via_seq<'a, A: 'a, B: 'a, C: 'a>( f: impl 'a + FnOnce(A, B) -> C, diff --git a/src/func/test_suite.rs b/src/func/test_suite.rs index 33254eb..db12779 100644 --- a/src/func/test_suite.rs +++ b/src/func/test_suite.rs @@ -3,7 +3,8 @@ use super::{tests::*, *}; pub trait FunctorTestSuite: WeakFunctor + Eqr { fn sample<'a, A: 'a, F: FnMut(&'a dyn Fn(A) -> Self::F<'a, A>)>(f: F) where - Self::F<'a, A>: 'a, Self: 'a; + Self::F<'a, A>: 'a, + Self: 'a; } pub fn functor_follows_laws() -> R { diff --git a/src/std/atomic.rs b/src/std/atomic.rs index ae21dc8..9415821 100644 --- a/src/std/atomic.rs +++ b/src/std/atomic.rs @@ -5,14 +5,14 @@ use std::{error::Error, marker::PhantomData, rc::Rc}; use crate::core::*; -/// This trait combines functionality of [Mentionable] and [Factory], -/// while limiting [Mentionable::points] (and corresponding [Mentionable::topology]) to an empty sequence. +/// This trait combines functionality of [`Mentionable`] and [`Factory`], +/// while limiting [`Mentionable::points`] (and corresponding [`Mentionable::topology`]) to an empty sequence. pub trait Atomic: 'static + Sized + Clone + Serializable { - /// Equivalent of [Factory::ParseError]. + /// Equivalent of [`Factory::ParseError`]. type ParseError: Error; - /// Static equivalent of [Factory::deserialize]. + /// Static equivalent of [`Factory::deserialize`]. fn deserialize(deserializer: &mut dyn Deserializer) -> Result; - /// Static equivalent of [Factory::unexpected_tail]. + /// Static equivalent of [`Factory::unexpected_tail`]. fn unexpected_tail(tail: &[u8]) -> Self::ParseError; } diff --git a/src/std/inlining.rs b/src/std/inlining.rs index e69de29..8b13789 100644 --- a/src/std/inlining.rs +++ b/src/std/inlining.rs @@ -0,0 +1 @@ + diff --git a/src/std/nullable.rs b/src/std/nullable.rs index cba7bee..757e849 100644 --- a/src/std/nullable.rs +++ b/src/std/nullable.rs @@ -1,4 +1,4 @@ -//! This module introduces [Option]-like concepts into RADN typesystem using [Nullable]. +//! This module introduces [`Option`]-like concepts into RADN typesystem using [`Nullable`]. use crate::core::*; use crate::std::*;