func docs

This commit is contained in:
AF 2023-04-21 20:58:13 +00:00
parent 4ad05fbe7c
commit a5fef129fe
15 changed files with 166 additions and 22 deletions

View File

@ -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:
//! * <https://hackage.haskell.org/package/base-4.18.0.0/docs/Data-Functor.html>
//! * <https://hackage.haskell.org/package/base-4.18.0.0/docs/Control-Applicative.html>
//! * <https://hackage.haskell.org/package/base-4.18.0.0/docs/Control-Monad.html>
pub mod classes; pub mod classes;
pub mod clone_func; pub mod clone_func;
pub mod copy_func; pub mod copy_func;
@ -7,13 +16,16 @@ pub mod test_suite;
#[cfg(test)] #[cfg(test)]
pub mod tests; pub mod tests;
/// Part of Haskell's `Functor f` responsible to use `f a`.
///
/// <https://hackage.haskell.org/package/base-4.18.0.0/docs/Data-Functor.html>
pub trait WeakFunctor { pub trait WeakFunctor {
type F<'a, A: 'a>: 'a type F<'a, A: 'a>: 'a
where where
Self: 'a; 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: /// Cannot insantiate for e.g. multi-element collections:
/// ```compile_fail /// ```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 /// 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::*; /// use radn_rs::func::clone_func::*;
/// ///
@ -52,12 +64,17 @@ pub trait WeakFunctor {
/// } /// }
/// } /// }
/// ``` /// ```
///
/// <https://hackage.haskell.org/package/base-4.18.0.0/docs/Data-Functor.html>
pub trait Functor: 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> fn fmap<'a, A: 'a, B: 'a>(f: impl 'a + FnOnce(A) -> B, fa: Self::F<'a, A>) -> Self::F<'a, B>
where where
Self: 'a; Self: 'a;
/// Equivalent of Haskell's `$>`/`<$`.
fn replace<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, b: B) -> Self::F<'a, B> fn replace<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, b: B) -> Self::F<'a, B>
where where
Self: 'a, Self: 'a,
@ -65,6 +82,7 @@ pub trait Functor: WeakFunctor {
Self::fmap(|_| b, fa) Self::fmap(|_| b, fa)
} }
/// Equivalent of Haskell's `void`.
fn void<'a, A: 'a>(fa: Self::F<'a, A>) -> Self::F<'a, ()> fn void<'a, A: 'a>(fa: Self::F<'a, A>) -> Self::F<'a, ()>
where where
Self: 'a, Self: 'a,
@ -73,13 +91,9 @@ pub trait Functor: WeakFunctor {
} }
} }
pub fn fmap<'a, T: 'a + Functor, A: 'a, B: 'a>( /// Part of [`Applicative`] responsible for Haskell's sequential application `<*>`.
f: impl 'a + FnOnce(A) -> B,
) -> impl FnOnce(T::F<'a, A>) -> T::F<'a, B> {
move |fa| T::fmap(f, fa)
}
pub trait ApplicativeSeq: Functor { pub trait ApplicativeSeq: Functor {
/// Equivalent of Haskell's `<*>`.
fn seq<'a, A: 'a, B: 'a>( fn seq<'a, A: 'a, B: 'a>(
ff: Self::F<'a, impl 'a + FnOnce(A) -> B>, ff: Self::F<'a, impl 'a + FnOnce(A) -> B>,
fa: Self::F<'a, A>, fa: Self::F<'a, A>,
@ -88,7 +102,9 @@ pub trait ApplicativeSeq: Functor {
Self: 'a; Self: 'a;
} }
/// Part of [`Applicative`] responsible for Haskell's result combination `listA2`.
pub trait ApplicativeLA2: Functor { pub trait ApplicativeLA2: Functor {
/// Equivalent of Haskell's `listA2`.
fn la2<'a, A: 'a, B: 'a, C: 'a>( fn la2<'a, A: 'a, B: 'a, C: 'a>(
f: impl 'a + FnOnce(A, B) -> C, f: impl 'a + FnOnce(A, B) -> C,
fa: Self::F<'a, A>, fa: Self::F<'a, A>,
@ -98,15 +114,23 @@ pub trait ApplicativeLA2: Functor {
Self: 'a; Self: 'a;
} }
/// Part of [`Applicative`] responsible for Rust-style result combination, specifically for tuples.
pub trait ApplicativeTuple: Functor { 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)> fn tuple<'a, A: 'a, B: 'a>(fab: (Self::F<'a, A>, Self::F<'a, B>)) -> Self::F<'a, (A, B)>
where where
Self: 'a; Self: 'a;
} }
/// Equivalent of Haskell's `Applicative`.
/// Split into [`ApplicativeSeq`], [`ApplicativeLA2`] and [`ApplicativeTuple`] due to Rust limitations.
///
/// <https://hackage.haskell.org/package/base-4.18.0.0/docs/Control-Applicative.html>
pub trait Applicative: Functor + ApplicativeSeq + ApplicativeLA2 + ApplicativeTuple { pub trait Applicative: Functor + ApplicativeSeq + ApplicativeLA2 + ApplicativeTuple {
/// Equivalent of Haskell's `pure`/`return`.
fn pure<'a, A: 'a>(a: A) -> Self::F<'a, A>; 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> fn discard_first<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, fb: Self::F<'a, B>) -> Self::F<'a, B>
where where
Self: 'a, Self: 'a,
@ -114,6 +138,7 @@ pub trait Applicative: Functor + ApplicativeSeq + ApplicativeLA2 + ApplicativeTu
Self::seq(Self::replace(fa, |b| b), fb) 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> fn discard_second<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, fb: Self::F<'a, B>) -> Self::F<'a, A>
where where
Self: 'a, Self: 'a,
@ -122,7 +147,11 @@ pub trait Applicative: Functor + ApplicativeSeq + ApplicativeLA2 + ApplicativeTu
} }
} }
/// Equivalent of Haskell's `Monad`.
///
/// <https://hackage.haskell.org/package/base-4.18.0.0/docs/Control-Monad.html>
pub trait Monad: Applicative { pub trait Monad: Applicative {
/// Equivalent of Haskell's `>==`.
fn bind<'a, A: 'a, B: 'a>( fn bind<'a, A: 'a, B: 'a>(
fa: Self::F<'a, A>, fa: Self::F<'a, A>,
f: impl 'a + FnOnce(A) -> Self::F<'a, B>, f: impl 'a + FnOnce(A) -> Self::F<'a, B>,
@ -130,6 +159,7 @@ pub trait Monad: Applicative {
where where
Self: 'a; Self: 'a;
/// Equivalent of Haskell's `join`.
fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A>
where where
Self::F<'a, A>: 'a, Self::F<'a, A>: 'a,
@ -139,12 +169,28 @@ pub trait Monad: Applicative {
} }
} }
/// Equivalent of Haskell's `MonadFail`.
///
/// <https://hackage.haskell.org/package/base-4.18.0.0/docs/Control-Monad.html>
pub trait MonadFail<E>: Monad { pub trait MonadFail<E>: 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.
///
/// <https://hackage.haskell.org/package/base-4.18.0.0/docs/Control-Applicative.html>
pub trait Alternative: Applicative { 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;
} }

View File

@ -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<E>`] 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 composition;
pub mod future; pub mod future;
pub mod lazy; pub mod lazy;

View File

@ -1,3 +1,8 @@
//! Async [Monad] based on [`Pin<Box<dyn Future>>`] (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 std::{future::Future, pin::Pin};
use futures::join; use futures::join;

View File

@ -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::*; use crate::func::*;
struct LazyClass; pub struct LazyClass;
impl WeakFunctor for LazyClass { impl WeakFunctor for LazyClass {
type F<'a, A: 'a> = Box<dyn 'a + FnOnce() -> A>; type F<'a, A: 'a> = Box<dyn 'a + FnOnce() -> A>;

View File

@ -1,3 +1,12 @@
//! Implementation of [`Monad`] for [`Option<A>`].
//!
//! 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<A, E>`] alternative see [`super::result`]
use crate::func::*; use crate::func::*;
pub struct OptionClass; pub struct OptionClass;

View File

@ -1,3 +1,12 @@
//! Implementation of [`MonadFail<E>`] for [`Result<A, E>`].
//!
//! 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<A>`] alternative see [`super::option`]
use crate::func::*; use crate::func::*;
pub struct ResultClass<E: 'static>(E); pub struct ResultClass<E: 'static>(E);
@ -72,3 +81,12 @@ impl<E> Monad for ResultClass<E> {
ffa? ffa?
} }
} }
impl<E> MonadFail<E> for ResultClass<E> {
fn fail<'a, A: 'a>(e: E) -> Self::F<'a, A>
where
Self: 'a,
{
Err(e)
}
}

View File

@ -1,3 +1,7 @@
//! Simplest sync [Monad].
//!
//! For async, see [`super::future`].
use crate::func::*; use crate::func::*;
pub struct SoloClass; pub struct SoloClass;

View File

@ -1,3 +1,8 @@
//! Helper [Monad]s to move deep execution chains off the stack onto the heap.
//! [`Stackless<A>`] represents a wrapped value.
//!
//! For lazy stackful execution see [`super::lazy`].
use std::{cell::Cell, rc::Rc}; use std::{cell::Cell, rc::Rc};
use crate::func::derivations::*; use crate::func::derivations::*;
@ -50,7 +55,9 @@ impl<'a, A: 'a> Stackless<'a, A> {
self.0(Box::new(f)) self.0(Box::new(f))
} }
fn bind<B: 'a>(self, f: impl 'a + FnOnce(A) -> Stackless<'a, B>) -> Stackless<'a, B> { /// Method-like equivalent of [`Monad::bind`],
/// the preferred way to chain [`Stackless<A>`] and `FnOnce(A) -> Stackless<B>` into [`Stackless<B>`].
pub fn bind<B: 'a>(self, f: impl 'a + FnOnce(A) -> Stackless<'a, B>) -> Stackless<'a, B> {
Stackless(Box::new(|takesb| { Stackless(Box::new(|takesb| {
let lcell = Rc::new(Cell::new(None)); let lcell = Rc::new(Cell::new(None));
let rcell = lcell.clone(); let rcell = lcell.clone();
@ -66,7 +73,8 @@ impl<'a, A: 'a> Stackless<'a, A> {
})) }))
} }
fn map<B: 'a>(self, f: impl 'a + FnOnce(A) -> B) -> Stackless<'a, B> { /// Method-like equivalent of [`Functor::fmap`].
pub fn map<B: 'a>(self, f: impl 'a + FnOnce(A) -> B) -> Stackless<'a, B> {
Stackless(Box::new(|takesb| { Stackless(Box::new(|takesb| {
let lcell = Rc::new(Cell::new(None)); let lcell = Rc::new(Cell::new(None));
let rcell = lcell.clone(); 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 { pub fn evaluate(self) -> A {
let ocell = Rc::new(Cell::new(None)); let ocell = Rc::new(Cell::new(None));
let icell = ocell.clone(); let icell = ocell.clone();

View File

@ -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 { pub trait CloneWeakFunctor {
type ClF<'a, A: Clone>: Clone; type ClF<'a, A: Clone>: Clone;
} }

View File

@ -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 { pub trait CopyWeakFunctor {
type CF<'a, A: Copy>: Copy; type CF<'a, A: Copy>: Copy;
} }

View File

@ -1,5 +1,14 @@
//! Useful helper functions/methods to extrapolate the existing behaviour.
use crate::func::*; 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 { pub trait ApplicativeLA2ViaSeq: ApplicativeSeq {
fn _la2_via_seq<'a, A: 'a, B: 'a, C: 'a>( fn _la2_via_seq<'a, A: 'a, B: 'a, C: 'a>(
f: impl 'a + FnOnce(A, B) -> C, f: impl 'a + FnOnce(A, B) -> C,

View File

@ -3,7 +3,8 @@ use super::{tests::*, *};
pub trait FunctorTestSuite: WeakFunctor + Eqr { pub trait FunctorTestSuite: WeakFunctor + Eqr {
fn sample<'a, A: 'a, F: FnMut(&'a dyn Fn(A) -> Self::F<'a, A>)>(f: F) fn sample<'a, A: 'a, F: FnMut(&'a dyn Fn(A) -> Self::F<'a, A>)>(f: F)
where where
Self::F<'a, A>: 'a, Self: 'a; Self::F<'a, A>: 'a,
Self: 'a;
} }
pub fn functor_follows_laws<T: Functor + FunctorTestSuite>() -> R { pub fn functor_follows_laws<T: Functor + FunctorTestSuite>() -> R {

View File

@ -5,14 +5,14 @@ use std::{error::Error, marker::PhantomData, rc::Rc};
use crate::core::*; use crate::core::*;
/// This trait combines functionality of [Mentionable] and [Factory], /// This trait combines functionality of [`Mentionable`] and [`Factory`],
/// while limiting [Mentionable::points] (and corresponding [Mentionable::topology]) to an empty sequence. /// while limiting [`Mentionable::points`] (and corresponding [`Mentionable::topology`]) to an empty sequence.
pub trait Atomic: 'static + Sized + Clone + Serializable { pub trait Atomic: 'static + Sized + Clone + Serializable {
/// Equivalent of [Factory::ParseError]. /// Equivalent of [`Factory::ParseError`].
type ParseError: Error; type ParseError: Error;
/// Static equivalent of [Factory::deserialize]. /// Static equivalent of [`Factory::deserialize`].
fn deserialize(deserializer: &mut dyn Deserializer) -> Result<Self, Self::ParseError>; fn deserialize(deserializer: &mut dyn Deserializer) -> Result<Self, Self::ParseError>;
/// Static equivalent of [Factory::unexpected_tail]. /// Static equivalent of [`Factory::unexpected_tail`].
fn unexpected_tail(tail: &[u8]) -> Self::ParseError; fn unexpected_tail(tail: &[u8]) -> Self::ParseError;
} }

View File

@ -0,0 +1 @@

View File

@ -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::core::*;
use crate::std::*; use crate::std::*;