lifetimes + FutureClass
This commit is contained in:
parent
5297261091
commit
445fa00165
@ -6,3 +6,4 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
futures = "0.3.26"
|
||||
|
168
src/copy_func.rs
168
src/copy_func.rs
@ -1,141 +1,147 @@
|
||||
use super::func::*;
|
||||
|
||||
pub trait CopyWeakFunctor {
|
||||
type CF<A: Copy>;
|
||||
type CF<'a, A: Copy>;
|
||||
}
|
||||
|
||||
pub trait CopyFunctor: CopyWeakFunctor {
|
||||
fn copy_fmap<A: Copy, B: Copy, F: Fn(A) -> B>(f: F, fa: Self::CF<A>) -> Self::CF<B>;
|
||||
fn copy_fmap<'a, A: 'a + Copy, B: 'a + Copy, F: 'a + Fn(A) -> B>(
|
||||
f: F,
|
||||
fa: Self::CF<'a, A>,
|
||||
) -> Self::CF<'a, B>;
|
||||
|
||||
fn copy_replace<A: Copy, B: Copy>(fa: Self::CF<A>, b: B) -> Self::CF<B> {
|
||||
Self::copy_fmap(|_| b, fa)
|
||||
fn copy_replace<'a, A: 'a + Copy, B: 'a + Copy>(fa: Self::CF<'a, A>, b: B) -> Self::CF<'a, B> {
|
||||
Self::copy_fmap(move |_| b, fa)
|
||||
}
|
||||
|
||||
fn copy_void<A: Copy>(fa: Self::CF<A>) -> Self::CF<()> {
|
||||
fn copy_void<'a, A: 'a + Copy>(fa: Self::CF<'a, A>) -> Self::CF<'a, ()> {
|
||||
Self::copy_replace(fa, ())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: WeakFunctor> CopyWeakFunctor for T {
|
||||
type CF<A: Copy> = T::F<A>;
|
||||
type CF<'a, A: Copy> = T::F<'a, A>;
|
||||
}
|
||||
|
||||
impl<T: Functor> CopyFunctor for T {
|
||||
fn copy_fmap<A: Copy, B: Copy, F: Fn(A) -> B>(f: F, fa: Self::CF<A>) -> Self::CF<B> {
|
||||
fn copy_fmap<'a, A: 'a + Copy, B: 'a + Copy, F: 'a + Fn(A) -> B>(
|
||||
f: F,
|
||||
fa: Self::CF<'a, A>,
|
||||
) -> Self::CF<'a, B> {
|
||||
Self::fmap(f, fa)
|
||||
}
|
||||
|
||||
fn copy_replace<A: Copy, B: Copy>(fa: Self::CF<A>, b: B) -> Self::CF<B> {
|
||||
fn copy_replace<'a, A: 'a + Copy, B: 'a + Copy>(fa: Self::CF<'a, A>, b: B) -> Self::CF<'a, B> {
|
||||
Self::replace(fa, b)
|
||||
}
|
||||
|
||||
fn copy_void<A: Copy>(fa: Self::CF<A>) -> Self::CF<()> {
|
||||
fn copy_void<'a, A: 'a + Copy>(fa: Self::CF<'a, A>) -> Self::CF<'a, ()> {
|
||||
Self::void(fa)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CopyApplicativeSeq: CopyFunctor {
|
||||
fn copy_seq<A: Copy, B: Copy, F: Copy + Fn(A) -> B>(
|
||||
ff: Self::CF<F>,
|
||||
fa: Self::CF<A>,
|
||||
) -> Self::CF<B>;
|
||||
fn copy_seq<'a, A: 'a + Copy, B: 'a + Copy, F: 'a + Copy + Fn(A) -> B>(
|
||||
ff: Self::CF<'a, F>,
|
||||
fa: Self::CF<'a, A>,
|
||||
) -> Self::CF<'a, B>;
|
||||
|
||||
fn _copy_la2<A: Copy, B: Copy, C: Copy, F: Copy + Fn(A, B) -> C>(
|
||||
fn _copy_la2<'a, A: 'a + Copy, B: 'a + Copy, C: 'a + Copy, F: 'a + Copy + Fn(A, B) -> C>(
|
||||
f: F,
|
||||
fa: Self::CF<A>,
|
||||
fb: Self::CF<B>,
|
||||
) -> Self::CF<C> {
|
||||
Self::copy_seq(Self::copy_fmap(|a| move |b| f(a, b), fa), fb)
|
||||
fa: Self::CF<'a, A>,
|
||||
fb: Self::CF<'a, B>,
|
||||
) -> Self::CF<'a, C> {
|
||||
Self::copy_seq(Self::copy_fmap(move |a| move |b| f(a, b), fa), fb)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ApplicativeSeq> CopyApplicativeSeq for T {
|
||||
fn copy_seq<A: Copy, B: Copy, F: Copy + Fn(A) -> B>(
|
||||
ff: Self::CF<F>,
|
||||
fa: Self::CF<A>,
|
||||
) -> Self::CF<B> {
|
||||
fn copy_seq<'a, A: 'a + Copy, B: 'a + Copy, F: 'a + Copy + Fn(A) -> B>(
|
||||
ff: Self::CF<'a, F>,
|
||||
fa: Self::CF<'a, A>,
|
||||
) -> Self::CF<'a, B> {
|
||||
Self::seq(ff, fa)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CopyApplicativeLA2: CopyFunctor {
|
||||
fn copy_la2<A: Copy, B: Copy, C: Copy, F: Fn(A, B) -> C>(
|
||||
fn copy_la2<'a, A: 'a + Copy, B: 'a + Copy, C: 'a + Copy, F: 'a + Copy + Fn(A, B) -> C>(
|
||||
f: F,
|
||||
fa: Self::CF<A>,
|
||||
fb: Self::CF<B>,
|
||||
) -> Self::CF<C>;
|
||||
fa: Self::CF<'a, A>,
|
||||
fb: Self::CF<'a, B>,
|
||||
) -> Self::CF<'a, C>;
|
||||
|
||||
fn _copy_seq<A: Copy, B: Copy, F: Copy + Fn(A) -> B>(
|
||||
ff: Self::CF<F>,
|
||||
fa: Self::CF<A>,
|
||||
) -> Self::CF<B> {
|
||||
fn _copy_seq<'a, A: 'a + Copy, B: 'a + Copy, F: 'a + Copy + Fn(A) -> B>(
|
||||
ff: Self::CF<'a, F>,
|
||||
fa: Self::CF<'a, A>,
|
||||
) -> Self::CF<'a, B> {
|
||||
Self::copy_la2(|f, a| f(a), ff, fa)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ApplicativeLA2> CopyApplicativeLA2 for T {
|
||||
fn copy_la2<A: Copy, B: Copy, C: Copy, F: Fn(A, B) -> C>(
|
||||
fn copy_la2<'a, A: 'a + Copy, B: 'a + Copy, C: 'a + Copy, F: 'a + Copy + Fn(A, B) -> C>(
|
||||
f: F,
|
||||
fa: Self::CF<A>,
|
||||
fb: Self::CF<B>,
|
||||
) -> Self::CF<C> {
|
||||
fa: Self::CF<'a, A>,
|
||||
fb: Self::CF<'a, B>,
|
||||
) -> Self::CF<'a, C> {
|
||||
Self::la2(f, fa, fb)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CopyApplicative: CopyFunctor + CopyApplicativeSeq + CopyApplicativeLA2 {
|
||||
fn copy_pure<A: Copy>(a: A) -> Self::CF<A>;
|
||||
// pub trait CopyApplicative: CopyFunctor + CopyApplicativeSeq + CopyApplicativeLA2 {
|
||||
// fn copy_pure<A: Copy>(a: A) -> Self::CF<A>;
|
||||
|
||||
fn copy_discard_first<A: Copy, B: Copy>(fa: Self::CF<A>, fb: Self::CF<B>) -> Self::CF<B> {
|
||||
Self::copy_seq(Self::copy_replace(fa, |b| b), fb)
|
||||
}
|
||||
// fn copy_discard_first<A: Copy, B: Copy>(fa: Self::CF<A>, fb: Self::CF<B>) -> Self::CF<B> {
|
||||
// Self::copy_seq(Self::copy_replace(fa, |b| b), fb)
|
||||
// }
|
||||
|
||||
fn copy_discard_second<A: Copy, B: Copy>(fa: Self::CF<A>, fb: Self::CF<B>) -> Self::CF<A> {
|
||||
Self::copy_la2(|a, _| a, fa, fb)
|
||||
}
|
||||
}
|
||||
// fn copy_discard_second<A: Copy, B: Copy>(fa: Self::CF<A>, fb: Self::CF<B>) -> Self::CF<A> {
|
||||
// Self::copy_la2(|a, _| a, fa, fb)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<T: Applicative> CopyApplicative for T {
|
||||
fn copy_pure<A: Copy>(a: A) -> Self::CF<A> {
|
||||
Self::pure(a)
|
||||
}
|
||||
// impl<T: Applicative> CopyApplicative for T {
|
||||
// fn copy_pure<A: Copy>(a: A) -> Self::CF<A> {
|
||||
// Self::pure(a)
|
||||
// }
|
||||
|
||||
fn copy_discard_first<A: Copy, B: Copy>(fa: Self::CF<A>, fb: Self::CF<B>) -> Self::CF<B> {
|
||||
Self::discard_first(fa, fb)
|
||||
}
|
||||
// fn copy_discard_first<A: Copy, B: Copy>(fa: Self::CF<A>, fb: Self::CF<B>) -> Self::CF<B> {
|
||||
// Self::discard_first(fa, fb)
|
||||
// }
|
||||
|
||||
fn copy_discard_second<A: Copy, B: Copy>(fa: Self::CF<A>, fb: Self::CF<B>) -> Self::CF<A> {
|
||||
Self::discard_second(fa, fb)
|
||||
}
|
||||
}
|
||||
// fn copy_discard_second<A: Copy, B: Copy>(fa: Self::CF<A>, fb: Self::CF<B>) -> Self::CF<A> {
|
||||
// Self::discard_second(fa, fb)
|
||||
// }
|
||||
// }
|
||||
|
||||
pub trait CopyMonad: CopyApplicative {
|
||||
fn copy_bind<A: Copy, B: Copy, F: Copy + Fn(A) -> Self::CF<B>>(
|
||||
fa: Self::CF<A>,
|
||||
f: F,
|
||||
) -> Self::CF<B>;
|
||||
// pub trait CopyMonad: CopyApplicative {
|
||||
// fn copy_bind<A: Copy, B: Copy, F: Copy + Fn(A) -> Self::CF<B>>(
|
||||
// fa: Self::CF<A>,
|
||||
// f: F,
|
||||
// ) -> Self::CF<B>;
|
||||
|
||||
fn copy_join<A: Copy>(ffa: Self::CF<Self::CF<A>>) -> Self::CF<A>
|
||||
where
|
||||
Self::CF<A>: Copy,
|
||||
{
|
||||
// ugly
|
||||
Self::copy_bind(ffa, |fa| fa)
|
||||
}
|
||||
}
|
||||
// fn copy_join<A: Copy>(ffa: Self::CF<Self::CF<A>>) -> Self::CF<A>
|
||||
// where
|
||||
// Self::CF<A>: Copy,
|
||||
// {
|
||||
// // ugly
|
||||
// Self::copy_bind(ffa, |fa| fa)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<T: Monad> CopyMonad for T {
|
||||
fn copy_bind<A: Copy, B: Copy, F: Copy + Fn(A) -> Self::CF<B>>(
|
||||
fa: Self::CF<A>,
|
||||
f: F,
|
||||
) -> Self::CF<B> {
|
||||
T::bind(fa, f)
|
||||
}
|
||||
// impl<T: Monad> CopyMonad for T {
|
||||
// fn copy_bind<A: Copy, B: Copy, F: Copy + Fn(A) -> Self::CF<B>>(
|
||||
// fa: Self::CF<A>,
|
||||
// f: F,
|
||||
// ) -> Self::CF<B> {
|
||||
// T::bind(fa, f)
|
||||
// }
|
||||
|
||||
fn copy_join<A: Copy>(ffa: Self::CF<Self::CF<A>>) -> Self::CF<A>
|
||||
where
|
||||
Self::CF<A>: Copy,
|
||||
{
|
||||
T::join(ffa)
|
||||
}
|
||||
}
|
||||
// fn copy_join<A: Copy>(ffa: Self::CF<Self::CF<A>>) -> Self::CF<A>
|
||||
// where
|
||||
// Self::CF<A>: Copy,
|
||||
// {
|
||||
// T::join(ffa)
|
||||
// }
|
||||
// }
|
||||
|
478
src/func.rs
478
src/func.rs
@ -1,476 +1,76 @@
|
||||
pub mod classes;
|
||||
#[cfg(test)]
|
||||
pub mod tests;
|
||||
#[cfg(test)]
|
||||
pub mod test_suite;
|
||||
|
||||
pub trait WeakFunctor {
|
||||
type F<A>;
|
||||
type F<'a, A>;
|
||||
}
|
||||
|
||||
pub trait Functor: WeakFunctor {
|
||||
fn fmap<A, B, F: FnOnce(A) -> B>(f: F, fa: Self::F<A>) -> Self::F<B>;
|
||||
fn fmap<'a, A: 'a, B: 'a, F: 'a + FnOnce(A) -> B>(f: F, fa: Self::F<'a, A>) -> Self::F<'a, B>;
|
||||
|
||||
fn replace<A, B>(fa: Self::F<A>, b: B) -> Self::F<B> {
|
||||
fn replace<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, b: B) -> Self::F<'a, B> {
|
||||
Self::fmap(|_| b, fa)
|
||||
}
|
||||
|
||||
fn void<A>(fa: Self::F<A>) -> Self::F<()> {
|
||||
fn void<'a, A: 'a>(fa: Self::F<'a, A>) -> Self::F<'a, ()> {
|
||||
Self::replace(fa, ())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ApplicativeSeq: Functor {
|
||||
fn seq<A, B, F: FnOnce(A) -> B>(ff: Self::F<F>, fa: Self::F<A>) -> Self::F<B>;
|
||||
fn _la2<A, B, C, F: FnOnce(A, B) -> C>(f: F, fa: Self::F<A>, fb: Self::F<B>) -> Self::F<C> {
|
||||
fn seq<'a, A: 'a, B: 'a, F: 'a + FnOnce(A) -> B>(
|
||||
ff: Self::F<'a, F>,
|
||||
fa: Self::F<'a, A>,
|
||||
) -> Self::F<'a, B>;
|
||||
fn _la2<'a, A: 'a, B: 'a, C: 'a, F: 'a + FnOnce(A, B) -> C>(
|
||||
f: F,
|
||||
fa: Self::F<'a, A>,
|
||||
fb: Self::F<'a, B>,
|
||||
) -> Self::F<'a, C> {
|
||||
Self::seq(Self::fmap(|a| |b| f(a, b), fa), fb)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ApplicativeLA2: Functor {
|
||||
fn la2<A, B, C, F: FnOnce(A, B) -> C>(f: F, fa: Self::F<A>, fb: Self::F<B>) -> Self::F<C>;
|
||||
fn _seq<A, B, F: FnOnce(A) -> B>(ff: Self::F<F>, fa: Self::F<A>) -> Self::F<B> {
|
||||
fn la2<'a, A: 'a, B: 'a, C: 'a, F: 'a + FnOnce(A, B) -> C>(
|
||||
f: F,
|
||||
fa: Self::F<'a, A>,
|
||||
fb: Self::F<'a, B>,
|
||||
) -> Self::F<'a, C>;
|
||||
fn _seq<'a, A: 'a, B: 'a, F: 'a + FnOnce(A) -> B>(
|
||||
ff: Self::F<'a, F>,
|
||||
fa: Self::F<'a, A>,
|
||||
) -> Self::F<'a, B> {
|
||||
Self::la2(|f, a| f(a), ff, fa)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Applicative: Functor + ApplicativeSeq + ApplicativeLA2 {
|
||||
fn pure<A>(a: A) -> Self::F<A>;
|
||||
fn pure<'a, A: 'a>(a: A) -> Self::F<'a, A>;
|
||||
|
||||
fn discard_first<A, B>(fa: Self::F<A>, fb: Self::F<B>) -> Self::F<B> {
|
||||
fn discard_first<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, fb: Self::F<'a, B>) -> Self::F<'a, B> {
|
||||
Self::seq(Self::replace(fa, |b| b), fb)
|
||||
}
|
||||
|
||||
fn discard_second<A, B>(fa: Self::F<A>, fb: Self::F<B>) -> Self::F<A> {
|
||||
fn discard_second<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, fb: Self::F<'a, B>) -> Self::F<'a, A> {
|
||||
Self::la2(|a, _| a, fa, fb)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Monad: Applicative {
|
||||
fn bind<A, B, F: FnOnce(A) -> Self::F<B>>(fa: Self::F<A>, f: F) -> Self::F<B>;
|
||||
fn bind<'a, A: 'a, B: 'a, F: 'a + FnOnce(A) -> Self::F<'a, B>>(
|
||||
fa: Self::F<'a, A>,
|
||||
f: F,
|
||||
) -> Self::F<'a, B>;
|
||||
|
||||
fn join<A>(ffa: Self::F<Self::F<A>>) -> Self::F<A> {
|
||||
fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A>
|
||||
where
|
||||
Self::F<'a, A>: 'a,
|
||||
{
|
||||
Self::bind(ffa, |fa| fa)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use core::fmt::Debug;
|
||||
use std::ops::{Add, AddAssign};
|
||||
|
||||
pub struct TestResults(usize, usize, Result<(), String>);
|
||||
|
||||
pub type R = TestResults;
|
||||
|
||||
impl R {
|
||||
pub fn unwrap(self) {
|
||||
match self {
|
||||
TestResults(success, total, Err(e)) => panic!("failed:\n{success}/{total}\n{e}\n"),
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn eqr<T: PartialEq + Debug>(name: &str, left: T, right: T) -> R {
|
||||
if left == right {
|
||||
TestResults(1, 1, Ok(()))
|
||||
} else {
|
||||
TestResults(0, 1, Err(format!("{name}: {:?} != {:?}", left, right)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for R {
|
||||
fn default() -> Self {
|
||||
Self(0, 0, Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for R {
|
||||
type Output = R;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self(
|
||||
self.0 + rhs.0,
|
||||
self.1 + rhs.1,
|
||||
match (self.2, rhs.2) {
|
||||
(Err(le), Err(re)) => Err(le + "\n" + re.as_str()),
|
||||
(e, Ok(_)) => e,
|
||||
(Ok(_), e) => e,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<R> for R {
|
||||
fn add_assign(&mut self, rhs: R) {
|
||||
self.0 += rhs.0;
|
||||
self.1 += rhs.1;
|
||||
match (&mut self.2, rhs.2) {
|
||||
(Err(ref mut le), Err(re)) => {
|
||||
*le += "\n";
|
||||
*le += re.as_str()
|
||||
}
|
||||
(_, Ok(_)) => {}
|
||||
(lr, Err(re)) => *lr = Err(re),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmap_respects_identity<T: Functor, A, FA0: Fn() -> T::F<A>>(fa0: FA0) -> R
|
||||
where
|
||||
T::F<A>: Debug + PartialEq,
|
||||
{
|
||||
eqr("identity: fmap id == id", T::fmap(|a| a, fa0()), fa0())
|
||||
}
|
||||
|
||||
pub fn fmap_respects_composition<
|
||||
T: Functor,
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
F: Fn(B) -> C,
|
||||
G: Fn(A) -> B,
|
||||
FA0: Fn() -> T::F<A>,
|
||||
>(
|
||||
f: F,
|
||||
g: G,
|
||||
fa0: FA0,
|
||||
) -> R
|
||||
where
|
||||
T::F<C>: Debug + PartialEq,
|
||||
{
|
||||
eqr(
|
||||
"composition: fmap (f . g) == fmap f . fmap g",
|
||||
T::fmap(|a| f(g(a)), fa0()),
|
||||
T::fmap(f, T::fmap(g, fa0())),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn seq_respects_identity<T: Applicative, A, FA0: Fn() -> T::F<A>>(fa0: FA0) -> R
|
||||
where
|
||||
T::F<A>: Debug + PartialEq,
|
||||
{
|
||||
eqr(
|
||||
"identity: pure id <*> v = v",
|
||||
T::seq(T::pure(|a| a), fa0()),
|
||||
fa0(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn seq_respects_composition<
|
||||
T: Applicative,
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
F: Fn(B) -> C,
|
||||
G: Fn(A) -> B,
|
||||
FF0: Fn() -> T::F<F>,
|
||||
FG0: Fn() -> T::F<G>,
|
||||
FA0: Fn() -> T::F<A>,
|
||||
>(
|
||||
ff0: FF0,
|
||||
fg0: FG0,
|
||||
fa0: FA0,
|
||||
) -> R
|
||||
where
|
||||
T::F<C>: Debug + PartialEq,
|
||||
{
|
||||
eqr(
|
||||
"composition: pure (.) <*> u <*> v <*> w = u <*> (v <*> w)",
|
||||
T::seq(
|
||||
T::seq(
|
||||
T::seq(T::pure(|f: F| |g: G| move |a| f(g(a))), ff0()),
|
||||
fg0(),
|
||||
),
|
||||
fa0(),
|
||||
),
|
||||
T::seq(ff0(), T::seq(fg0(), fa0())),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn seq_is_homomorphic<T: Applicative, A, B, A0: Fn() -> A, F: Copy + Fn(A) -> B>(
|
||||
f: F,
|
||||
a0: A0,
|
||||
) -> R
|
||||
where
|
||||
T::F<B>: Debug + PartialEq,
|
||||
{
|
||||
eqr(
|
||||
"homomorphism: pure f <*> pure x = pure (f x)",
|
||||
T::seq(T::pure(f), T::pure(a0())),
|
||||
T::pure(f(a0())),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn seq_respects_interchange<
|
||||
T: Applicative,
|
||||
A,
|
||||
B,
|
||||
F: Fn(A) -> B,
|
||||
A0: Fn() -> A,
|
||||
FF0: Fn() -> T::F<F>,
|
||||
>(
|
||||
ff0: FF0,
|
||||
a0: A0,
|
||||
) -> R
|
||||
where
|
||||
T::F<B>: Debug + PartialEq,
|
||||
{
|
||||
eqr(
|
||||
"interchange: u <*> pure y = pure ($ y) <*> u",
|
||||
T::seq(ff0(), T::pure(a0())),
|
||||
T::seq(T::pure(|f: F| f(a0())), ff0()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn seq_can_be_expressed_via_la2<
|
||||
T: Applicative,
|
||||
A,
|
||||
B,
|
||||
F: Fn(A) -> B,
|
||||
FA0: Fn() -> T::F<A>,
|
||||
FF0: Fn() -> T::F<F>,
|
||||
>(
|
||||
ff0: FF0,
|
||||
fa0: FA0,
|
||||
) -> R
|
||||
where
|
||||
T::F<B>: Debug + PartialEq,
|
||||
{
|
||||
eqr(
|
||||
"seq via la2: (<*>) = liftA2 id",
|
||||
T::seq(ff0(), fa0()),
|
||||
T::la2(|f, a| f(a), ff0(), fa0()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn fmap_can_be_expressed_via_seq<
|
||||
T: Applicative,
|
||||
A,
|
||||
B,
|
||||
F: Copy + Fn(A) -> B,
|
||||
FA0: Fn() -> T::F<A>,
|
||||
>(
|
||||
f: F,
|
||||
fa0: FA0,
|
||||
) -> R
|
||||
where
|
||||
T::F<B>: Debug + PartialEq,
|
||||
{
|
||||
eqr(
|
||||
"fmap via seq: fmap f x = pure f <*> x",
|
||||
T::fmap(f, fa0()),
|
||||
T::seq(T::pure(f), fa0()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn discard_can_be_expressed_via_seq_or_la2<
|
||||
T: Applicative,
|
||||
A,
|
||||
B,
|
||||
FA0: Fn() -> T::F<A>,
|
||||
FB0: Fn() -> T::F<B>,
|
||||
>(
|
||||
fa0: FA0,
|
||||
fb0: FB0,
|
||||
) -> R
|
||||
where
|
||||
T::F<B>: Debug + PartialEq,
|
||||
{
|
||||
eqr(
|
||||
"discard via seq: u *> v = (id <$ u) <*> v",
|
||||
T::discard_first(fa0(), fb0()),
|
||||
T::seq(T::replace(fa0(), |b| b), fb0()),
|
||||
) + eqr(
|
||||
"discard via la2: u <* v = liftA2 const u v",
|
||||
T::discard_second(fb0(), fa0()),
|
||||
T::la2(|b, _| b, fb0(), fa0()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn bind_respects_left_identity<T: Monad, A, B, F: Copy + Fn(A) -> T::F<B>, A0: Fn() -> A>(
|
||||
f: F,
|
||||
a0: A0,
|
||||
) -> R
|
||||
where
|
||||
T::F<B>: Debug + PartialEq,
|
||||
{
|
||||
eqr(
|
||||
"left identity: pure a >>= k = k a",
|
||||
T::bind(T::pure(a0()), f),
|
||||
f(a0()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn bind_respects_right_identity<T: Monad, A, FA0: Fn() -> T::F<A>>(fa0: FA0) -> R
|
||||
where
|
||||
T::F<A>: Debug + PartialEq,
|
||||
{
|
||||
eqr(
|
||||
"right identity: m >>= bind = m",
|
||||
T::bind(fa0(), T::pure),
|
||||
fa0(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn bind_is_associative<
|
||||
T: Monad,
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
F: Copy + Fn(B) -> T::F<C>,
|
||||
G: Copy + Fn(A) -> T::F<B>,
|
||||
FA0: Fn() -> T::F<A>,
|
||||
>(
|
||||
f: F,
|
||||
g: G,
|
||||
fa0: FA0,
|
||||
) -> R
|
||||
where
|
||||
T::F<C>: Debug + PartialEq,
|
||||
{
|
||||
eqr(
|
||||
r"associativity: m >>= (\x -> k x >>= h) = (m >>= k) >>= h",
|
||||
T::bind(fa0(), |a| T::bind(g(a), f)),
|
||||
T::bind(T::bind(fa0(), g), f),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn seq_can_be_expressed_via_bind<
|
||||
T: Monad,
|
||||
A,
|
||||
B,
|
||||
F: Fn(A) -> B,
|
||||
FA0: Fn() -> T::F<A>,
|
||||
FF0: Fn() -> T::F<F>,
|
||||
>(
|
||||
ff0: FF0,
|
||||
fa0: FA0,
|
||||
) -> R
|
||||
where
|
||||
T::F<B>: Debug + PartialEq,
|
||||
{
|
||||
eqr(
|
||||
r"seq via bind: m1 <*> m2 = m1 >>= (\x1 -> m2 >>= (\x2 -> pure (x1 x2)))",
|
||||
T::seq(ff0(), fa0()),
|
||||
T::bind(ff0(), |f| T::bind(fa0(), |a| T::pure(f(a)))),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn fmap_can_be_expressed_via_bind<
|
||||
T: Monad,
|
||||
A,
|
||||
B,
|
||||
F: Copy + Fn(A) -> B,
|
||||
FA0: Fn() -> T::F<A>,
|
||||
>(
|
||||
f: F,
|
||||
fa0: FA0,
|
||||
) -> R
|
||||
where
|
||||
T::F<B>: Debug + PartialEq,
|
||||
{
|
||||
eqr(
|
||||
"fmap via bind: fmap f xs = xs >>= return . f",
|
||||
T::fmap(f, fa0()),
|
||||
T::bind(fa0(), |a| T::pure(f(a))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test_suite {
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::tests::*;
|
||||
use super::*;
|
||||
|
||||
pub trait FunctorTestSuite: WeakFunctor {
|
||||
fn sample<A>() -> Vec<Box<dyn Fn(A) -> Self::F<A>>>;
|
||||
}
|
||||
|
||||
pub fn functor_follows_laws<T: Functor + FunctorTestSuite>() -> R
|
||||
where
|
||||
T::F<i32>: Debug + PartialEq,
|
||||
{
|
||||
let mut res = R::default();
|
||||
for pa in T::sample::<_>() {
|
||||
res += fmap_respects_identity::<T, _, _>(|| pa(2));
|
||||
}
|
||||
for pa in T::sample::<_>() {
|
||||
res += fmap_respects_composition::<T, _, _, _, _, _, _>(|x| x + 5, |x| x + 3, || pa(2));
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
pub fn applicative_follows_laws<T: Applicative + FunctorTestSuite>() -> R
|
||||
where
|
||||
T::F<i32>: Debug + PartialEq,
|
||||
{
|
||||
let mut res = functor_follows_laws::<T>();
|
||||
for pa in T::sample::<_>() {
|
||||
res += seq_respects_identity::<T, _, _>(|| pa(2));
|
||||
}
|
||||
for pa in T::sample::<_>() {
|
||||
for pg in T::sample::<_>() {
|
||||
for pf in T::sample::<_>() {
|
||||
res += seq_respects_composition::<T, _, _, _, _, _, _, _, _>(
|
||||
|| pf(|x| x + 5),
|
||||
|| pg(|x| x + 3),
|
||||
|| pa(2),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
res += seq_is_homomorphic::<T, _, _, _, _>(|x| x + 3, || 2);
|
||||
for pf in T::sample::<_>() {
|
||||
res += seq_respects_interchange::<T, _, _, _, _, _>(|| pf(|x| x + 3), || 2);
|
||||
}
|
||||
for pa in T::sample::<_>() {
|
||||
for pf in T::sample::<_>() {
|
||||
res += seq_can_be_expressed_via_la2::<T, _, _, _, _, _>(|| pf(|x| x + 3), || pa(2));
|
||||
}
|
||||
}
|
||||
for pa in T::sample::<_>() {
|
||||
res += fmap_can_be_expressed_via_seq::<T, _, _, _, _>(|x| x + 3, || pa(2));
|
||||
}
|
||||
for pa in T::sample::<_>() {
|
||||
for pb in T::sample::<_>() {
|
||||
res += discard_can_be_expressed_via_seq_or_la2::<T, _, _, _, _>(|| pa(2), || pb(2));
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
pub fn monad_follows_laws<T: Monad + FunctorTestSuite>() -> R
|
||||
where
|
||||
T::F<i32>: Debug + PartialEq,
|
||||
{
|
||||
let mut res = applicative_follows_laws::<T>();
|
||||
for pa in T::sample::<_>() {
|
||||
res += bind_respects_left_identity::<T, _, _, _, _>(|x| pa(x + 3), || 2);
|
||||
}
|
||||
for pa in T::sample::<_>() {
|
||||
res += bind_respects_right_identity::<T, _, _>(|| pa(2));
|
||||
}
|
||||
for pa in T::sample::<_>() {
|
||||
for pg in T::sample::<_>() {
|
||||
for pf in T::sample::<_>() {
|
||||
res += bind_is_associative::<T, _, _, _, _, _, _>(
|
||||
|x| pf(x + 5),
|
||||
|x| pg(x + 3),
|
||||
|| pa(2),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
for pa in T::sample::<_>() {
|
||||
for pf in T::sample::<_>() {
|
||||
res += seq_can_be_expressed_via_bind::<T, _, _, _, _, _>(
|
||||
|| pf(|x| x + 3),
|
||||
|| pa(2),
|
||||
);
|
||||
}
|
||||
}
|
||||
for pa in T::sample::<_>() {
|
||||
res += fmap_can_be_expressed_via_bind::<T, _, _, _, _>(|x| x + 3, || pa(2));
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
2
src/func/classes.rs
Normal file
2
src/func/classes.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod optionclass;
|
||||
pub mod futureclass;
|
83
src/func/classes/futureclass.rs
Normal file
83
src/func/classes/futureclass.rs
Normal file
@ -0,0 +1,83 @@
|
||||
use std::{future::Future, pin::Pin};
|
||||
|
||||
// use futures::FutureExt;
|
||||
|
||||
use futures::join;
|
||||
|
||||
use crate::func::*;
|
||||
|
||||
pub struct FutureClass;
|
||||
|
||||
impl WeakFunctor for FutureClass {
|
||||
type F<'a, A> = Pin<Box<dyn 'a + Future<Output = A>>>;
|
||||
}
|
||||
|
||||
impl Functor for FutureClass {
|
||||
fn fmap<'a, A: 'a, B: 'a, F: 'a + FnOnce(A) -> B>(f: F, fa: Self::F<'a, A>) -> Self::F<'a, B> {
|
||||
Box::pin(async { f(fa.await) })
|
||||
}
|
||||
|
||||
fn replace<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, b: B) -> Self::F<'a, B> {
|
||||
Box::pin(async {
|
||||
fa.await;
|
||||
b
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicativeSeq for FutureClass {
|
||||
fn seq<'a, A: 'a, B: 'a, F: 'a + FnOnce(A) -> B>(
|
||||
ff: Self::F<'a, F>,
|
||||
fa: Self::F<'a, A>,
|
||||
) -> Self::F<'a, B> {
|
||||
Box::pin(async {
|
||||
let (f, a) = join!(ff, fa);
|
||||
f(a)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicativeLA2 for FutureClass {
|
||||
fn la2<'a, A: 'a, B: 'a, C: 'a, F: 'a + FnOnce(A, B) -> C>(
|
||||
f: F,
|
||||
fa: Self::F<'a, A>,
|
||||
fb: Self::F<'a, B>,
|
||||
) -> Self::F<'a, C> {
|
||||
Box::pin(async {
|
||||
let (a, b) = join!(fa, fb);
|
||||
f(a, b)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Applicative for FutureClass {
|
||||
fn pure<'a, A: 'a>(a: A) -> Self::F<'a, A> {
|
||||
Box::pin(async { a })
|
||||
}
|
||||
|
||||
fn discard_first<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, fb: Self::F<'a, B>) -> Self::F<'a, B> {
|
||||
Box::pin(async {
|
||||
join!(fa, fb).1
|
||||
})
|
||||
}
|
||||
|
||||
fn discard_second<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, fb: Self::F<'a, B>) -> Self::F<'a, A> {
|
||||
Box::pin(async {
|
||||
join!(fa, fb).0
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Monad for FutureClass {
|
||||
fn bind<'a, A: 'a, B: 'a, F: 'a + FnOnce(A) -> Self::F<'a, B>>(fa: Self::F<'a, A>, f: F) -> Self::F<'a, B> {
|
||||
Box::pin(async{
|
||||
f(fa.await).await
|
||||
})
|
||||
}
|
||||
|
||||
fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A> where Self::F<'a, A>: 'a {
|
||||
Box::pin(async{
|
||||
ffa.await.await
|
||||
})
|
||||
}
|
||||
}
|
@ -3,18 +3,18 @@ use crate::func::*;
|
||||
pub struct OptionClass;
|
||||
|
||||
impl WeakFunctor for OptionClass {
|
||||
type F<A> = Option<A>;
|
||||
type F<'_a, A> = Option<A>;
|
||||
}
|
||||
|
||||
impl Functor for OptionClass {
|
||||
fn fmap<A, B, F: FnOnce(A) -> B>(f: F, fa: Self::F<A>) -> Self::F<B> {
|
||||
fn fmap<'a, A: 'a, B: 'a, F: 'a + FnOnce(A) -> B>(f: F, fa: Self::F<'a, A>) -> Self::F<'a, B> {
|
||||
match fa {
|
||||
Some(a) => Some(f(a)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn replace<A, B>(fa: Self::F<A>, b: B) -> Self::F<B> {
|
||||
fn replace<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, b: B) -> Self::F<'a, B> {
|
||||
match fa {
|
||||
Some(_) => Some(b),
|
||||
None => None,
|
||||
@ -23,7 +23,10 @@ impl Functor for OptionClass {
|
||||
}
|
||||
|
||||
impl ApplicativeSeq for OptionClass {
|
||||
fn seq<A, B, F: FnOnce(A) -> B>(ff: Self::F<F>, fa: Self::F<A>) -> Self::F<B> {
|
||||
fn seq<'a, A: 'a, B: 'a, F: 'a + FnOnce(A) -> B>(
|
||||
ff: Self::F<'a, F>,
|
||||
fa: Self::F<'a, A>,
|
||||
) -> Self::F<'a, B> {
|
||||
match (ff, fa) {
|
||||
(Some(f), Some(a)) => Some(f(a)),
|
||||
_ => None,
|
||||
@ -32,7 +35,11 @@ impl ApplicativeSeq for OptionClass {
|
||||
}
|
||||
|
||||
impl ApplicativeLA2 for OptionClass {
|
||||
fn la2<A, B, C, F: FnOnce(A, B) -> C>(f: F, fa: Self::F<A>, fb: Self::F<B>) -> Self::F<C> {
|
||||
fn la2<'a, A: 'a, B: 'a, C: 'a, F: 'a + FnOnce(A, B) -> C>(
|
||||
f: F,
|
||||
fa: Self::F<'a, A>,
|
||||
fb: Self::F<'a, B>,
|
||||
) -> Self::F<'a, C> {
|
||||
match (fa, fb) {
|
||||
(Some(a), Some(b)) => Some(f(a, b)),
|
||||
_ => None,
|
||||
@ -41,18 +48,18 @@ impl ApplicativeLA2 for OptionClass {
|
||||
}
|
||||
|
||||
impl Applicative for OptionClass {
|
||||
fn pure<A>(a: A) -> Self::F<A> {
|
||||
fn pure<'a, A: 'a>(a: A) -> Self::F<'a, A> {
|
||||
Some(a)
|
||||
}
|
||||
|
||||
fn discard_first<A, B>(fa: Self::F<A>, fb: Self::F<B>) -> Self::F<B> {
|
||||
fn discard_first<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, fb: Self::F<'a, B>) -> Self::F<'a, B> {
|
||||
match fa {
|
||||
Some(_) => fb,
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn discard_second<A, B>(fa: Self::F<A>, fb: Self::F<B>) -> Self::F<A> {
|
||||
fn discard_second<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, fb: Self::F<'a, B>) -> Self::F<'a, A> {
|
||||
match fb {
|
||||
Some(_) => fa,
|
||||
None => None,
|
||||
@ -61,14 +68,20 @@ impl Applicative for OptionClass {
|
||||
}
|
||||
|
||||
impl Monad for OptionClass {
|
||||
fn bind<A, B, F: FnOnce(A) -> Self::F<B>>(fa: Self::F<A>, f: F) -> Self::F<B> {
|
||||
fn bind<'a, A: 'a, B: 'a, F: 'a + FnOnce(A) -> Self::F<'a, B>>(
|
||||
fa: Self::F<'a, A>,
|
||||
f: F,
|
||||
) -> Self::F<'a, B> {
|
||||
match fa {
|
||||
Some(a) => f(a),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn join<A>(ffa: Self::F<Self::F<A>>) -> Self::F<A> {
|
||||
fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A>
|
||||
where
|
||||
Self::F<'a, A>: 'a,
|
||||
{
|
||||
match ffa {
|
||||
Some(Some(a)) => Some(a),
|
||||
_ => None,
|
||||
@ -78,12 +91,22 @@ impl Monad for OptionClass {
|
||||
|
||||
#[cfg(test)]
|
||||
mod option_tests {
|
||||
use super::test_suite;
|
||||
use super::{test_suite, tests};
|
||||
|
||||
use super::{Functor, OptionClass as T};
|
||||
|
||||
impl tests::Eqr for T {
|
||||
fn eqr<'a, A: PartialEq + std::fmt::Debug>(
|
||||
name: &'a str,
|
||||
left: Self::F<'a, A>,
|
||||
right: Self::F<'a, A>,
|
||||
) -> tests::R {
|
||||
tests::eqr(name, left, right)
|
||||
}
|
||||
}
|
||||
|
||||
impl test_suite::FunctorTestSuite for T {
|
||||
fn sample<A>() -> Vec<Box<dyn Fn(A) -> Self::F<A>>> {
|
||||
fn sample<'a, A>() -> Vec<Box<dyn Fn(A) -> Self::F<'a, A>>> {
|
||||
vec![Box::new(|_| None), Box::new(|a| Some(a))]
|
||||
}
|
||||
}
|
88
src/func/test_suite.rs
Normal file
88
src/func/test_suite.rs
Normal file
@ -0,0 +1,88 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::{tests::*, *};
|
||||
|
||||
pub trait FunctorTestSuite: WeakFunctor + Eqr {
|
||||
fn sample<'a, A>() -> Vec<Box<dyn 'a + Fn(A) -> Self::F<'a, A>>>;
|
||||
}
|
||||
|
||||
pub fn functor_follows_laws<T: Functor + FunctorTestSuite>() -> R {
|
||||
let mut res = R::default();
|
||||
for pa in T::sample::<_>() {
|
||||
res += fmap_respects_identity::<T, _, _>(|| pa(2));
|
||||
}
|
||||
for pa in T::sample::<_>() {
|
||||
res += fmap_respects_composition::<T, _, _, _, _, _, _>(|x| x + 5, |x| x + 3, || pa(2));
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
pub fn applicative_follows_laws<T: Applicative + FunctorTestSuite>() -> R {
|
||||
let mut res = functor_follows_laws::<T>();
|
||||
for pa in T::sample::<_>() {
|
||||
res += seq_respects_identity::<T, _, _>(|| pa(2));
|
||||
}
|
||||
for pa in T::sample::<_>() {
|
||||
for pg in T::sample::<_>() {
|
||||
for pf in T::sample::<_>() {
|
||||
res += seq_respects_composition::<T, _, _, _, _, _, _, _, _>(
|
||||
|| pf(|x| x + 5),
|
||||
|| pg(|x| x + 3),
|
||||
|| pa(2),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
res += seq_is_homomorphic::<T, _, _, _, _>(|x| x + 3, || 2);
|
||||
for pf in T::sample::<_>() {
|
||||
res += seq_respects_interchange::<T, _, _, _, _, _>(|| pf(|x| x + 3), || 2);
|
||||
}
|
||||
for pa in T::sample::<_>() {
|
||||
for pf in T::sample::<_>() {
|
||||
res += seq_can_be_expressed_via_la2::<T, _, _, _, _, _>(|| pf(|x| x + 3), || pa(2));
|
||||
}
|
||||
}
|
||||
for pa in T::sample::<_>() {
|
||||
res += fmap_can_be_expressed_via_seq::<T, _, _, _, _>(|x| x + 3, || pa(2));
|
||||
}
|
||||
for pa in T::sample::<_>() {
|
||||
for pb in T::sample::<_>() {
|
||||
res += discard_can_be_expressed_via_seq_or_la2::<T, _, _, _, _>(|| pa(2), || pb(2));
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
pub fn monad_follows_laws<T: Monad + FunctorTestSuite>() -> R
|
||||
where {
|
||||
let mut res = applicative_follows_laws::<T>();
|
||||
for pa in T::sample::<_>() {
|
||||
res += bind_respects_left_identity::<T, _, _, _, _>(move |x| pa(x + 3), || 2);
|
||||
}
|
||||
for pa in T::sample::<_>() {
|
||||
res += bind_respects_right_identity::<T, _, _>(move || pa(2));
|
||||
}
|
||||
for pa in T::sample::<_>() {
|
||||
for pg in T::sample::<_>() {
|
||||
let pgrc = Rc::new(pg);
|
||||
for pf in T::sample::<_>() {
|
||||
let pgrci = pgrc.clone();
|
||||
let pfrc = Rc::new(pf);
|
||||
res += bind_is_associative::<T, _, _, _, _, _, _>(
|
||||
move |x| pfrc(x + 5),
|
||||
move |x| pgrci(x + 3),
|
||||
|| pa(2),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
for pf in T::sample::<_>() {
|
||||
for pa in T::sample::<_>() {
|
||||
seq_can_be_expressed_via_bind::<T, _, _, _, _, _>(|| pf(|x| x + 3), move || pa(2));
|
||||
}
|
||||
}
|
||||
for pa in T::sample::<_>() {
|
||||
res += fmap_can_be_expressed_via_bind::<T, _, _, _, _>(|x| x + 3, move || pa(2));
|
||||
}
|
||||
res
|
||||
}
|
352
src/func/tests.rs
Normal file
352
src/func/tests.rs
Normal file
@ -0,0 +1,352 @@
|
||||
use super::*;
|
||||
use core::fmt::Debug;
|
||||
use std::ops::{Add, AddAssign};
|
||||
|
||||
pub struct TestResults {
|
||||
success: usize,
|
||||
total: usize,
|
||||
result: Result<(), String>,
|
||||
}
|
||||
|
||||
pub type R = TestResults;
|
||||
|
||||
impl R {
|
||||
pub fn unwrap(self) {
|
||||
match self {
|
||||
TestResults {
|
||||
success,
|
||||
total,
|
||||
result: Err(e),
|
||||
} => panic!("failed:\n{success}/{total}\n{e}\n"),
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Eqr: WeakFunctor {
|
||||
fn eqr<'a, A: PartialEq + Debug>(
|
||||
name: &'a str,
|
||||
left: Self::F<'a, A>,
|
||||
right: Self::F<'a, A>,
|
||||
) -> R;
|
||||
}
|
||||
|
||||
pub fn eqr<T: PartialEq + Debug>(name: &str, left: T, right: T) -> R {
|
||||
if left == right {
|
||||
TestResults {
|
||||
success: 1,
|
||||
total: 1,
|
||||
result: Ok(()),
|
||||
}
|
||||
} else {
|
||||
TestResults {
|
||||
success: 0,
|
||||
total: 1,
|
||||
result: Err(format!("{name}: {:?} != {:?}", left, right)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for R {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
success: 0,
|
||||
total: 0,
|
||||
result: Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for R {
|
||||
type Output = R;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Self {
|
||||
success: self.success + rhs.success,
|
||||
total: self.total + rhs.total,
|
||||
result: match (self.result, rhs.result) {
|
||||
(Err(le), Err(re)) => Err(le + "\n" + re.as_str()),
|
||||
(e, Ok(_)) => e,
|
||||
(Ok(_), e) => e,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<R> for R {
|
||||
fn add_assign(&mut self, rhs: R) {
|
||||
self.success += rhs.success;
|
||||
self.total += rhs.total;
|
||||
match (&mut self.result, rhs.result) {
|
||||
(Err(ref mut le), Err(re)) => {
|
||||
*le += "\n";
|
||||
*le += re.as_str()
|
||||
}
|
||||
(_, Ok(_)) => {}
|
||||
(lr, Err(re)) => *lr = Err(re),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmap_respects_identity<
|
||||
'a,
|
||||
T: Functor + Eqr,
|
||||
A: 'a + Debug + PartialEq,
|
||||
FA0: Fn() -> T::F<'a, A>,
|
||||
>(
|
||||
fa0: FA0,
|
||||
) -> R {
|
||||
T::eqr("identity: fmap id == id", T::fmap(|a| a, fa0()), fa0())
|
||||
}
|
||||
|
||||
pub fn fmap_respects_composition<
|
||||
'a,
|
||||
T: Functor + Eqr,
|
||||
A: 'a,
|
||||
B: 'a,
|
||||
C: 'a + Debug + PartialEq,
|
||||
F: 'a + Copy + Fn(B) -> C,
|
||||
G: 'a + Copy + Fn(A) -> B,
|
||||
FA0: Fn() -> T::F<'a, A>,
|
||||
>(
|
||||
f: F,
|
||||
g: G,
|
||||
fa0: FA0,
|
||||
) -> R {
|
||||
T::eqr(
|
||||
"composition: fmap (f . g) == fmap f . fmap g",
|
||||
T::fmap(move |a| f(g(a)), fa0()),
|
||||
T::fmap(f, T::fmap(g, fa0())),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn seq_respects_identity<
|
||||
'a,
|
||||
T: Applicative + Eqr,
|
||||
A: 'a + Debug + PartialEq,
|
||||
FA0: Fn() -> T::F<'a, A>,
|
||||
>(
|
||||
fa0: FA0,
|
||||
) -> R {
|
||||
T::eqr(
|
||||
"identity: pure id <*> v = v",
|
||||
T::seq(T::pure(|a| a), fa0()),
|
||||
fa0(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn seq_respects_composition<
|
||||
'a,
|
||||
T: Applicative + Eqr,
|
||||
A: 'a,
|
||||
B: 'a,
|
||||
C: 'a + Debug + PartialEq,
|
||||
F: 'a + Fn(B) -> C,
|
||||
G: 'a + Fn(A) -> B,
|
||||
FF0: Fn() -> T::F<'a, F>,
|
||||
FG0: Fn() -> T::F<'a, G>,
|
||||
FA0: Fn() -> T::F<'a, A>,
|
||||
>(
|
||||
ff0: FF0,
|
||||
fg0: FG0,
|
||||
fa0: FA0,
|
||||
) -> R {
|
||||
T::eqr(
|
||||
"composition: pure (.) <*> u <*> v <*> w = u <*> (v <*> w)",
|
||||
T::seq(
|
||||
T::seq(
|
||||
T::seq(T::pure(|f: F| move |g: G| move |a| f(g(a))), ff0()),
|
||||
fg0(),
|
||||
),
|
||||
fa0(),
|
||||
),
|
||||
T::seq(ff0(), T::seq(fg0(), fa0())),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn seq_is_homomorphic<
|
||||
'a,
|
||||
T: Applicative + Eqr,
|
||||
A: 'a,
|
||||
B: 'a + Debug + PartialEq,
|
||||
A0: Fn() -> A,
|
||||
F: 'a + Fn(A) -> B,
|
||||
>(
|
||||
f: F,
|
||||
a0: A0,
|
||||
) -> R {
|
||||
T::eqr(
|
||||
"homomorphism: pure f <*> pure x = pure (f x)",
|
||||
T::pure(f(a0())),
|
||||
T::seq(T::pure(f), T::pure(a0())),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn seq_respects_interchange<
|
||||
'a,
|
||||
T: Applicative + Eqr,
|
||||
A: 'a,
|
||||
B: 'a + Debug + PartialEq,
|
||||
F: 'a + Fn(A) -> B,
|
||||
A0: 'a + Fn() -> A,
|
||||
FF0: Fn() -> T::F<'a, F>,
|
||||
>(
|
||||
ff0: FF0,
|
||||
a0: A0,
|
||||
) -> R {
|
||||
T::eqr(
|
||||
"interchange: u <*> pure y = pure ($ y) <*> u",
|
||||
T::seq(ff0(), T::pure(a0())),
|
||||
T::seq(T::pure(move |f: F| f(a0())), ff0()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn seq_can_be_expressed_via_la2<
|
||||
'a,
|
||||
T: Applicative + Eqr,
|
||||
A: 'a,
|
||||
B: 'a + Debug + PartialEq,
|
||||
F: 'a + Fn(A) -> B,
|
||||
FA0: Fn() -> T::F<'a, A>,
|
||||
FF0: Fn() -> T::F<'a, F>,
|
||||
>(
|
||||
ff0: FF0,
|
||||
fa0: FA0,
|
||||
) -> R {
|
||||
T::eqr(
|
||||
"seq via la2: (<*>) = liftA2 id",
|
||||
T::seq(ff0(), fa0()),
|
||||
T::la2(|f, a| f(a), ff0(), fa0()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn fmap_can_be_expressed_via_seq<
|
||||
'a,
|
||||
T: Applicative + Eqr,
|
||||
A: 'a,
|
||||
B: 'a + Debug + PartialEq,
|
||||
F: 'a + Copy + Fn(A) -> B,
|
||||
FA0: Fn() -> T::F<'a, A>,
|
||||
>(
|
||||
f: F,
|
||||
fa0: FA0,
|
||||
) -> R {
|
||||
T::eqr(
|
||||
"fmap via seq: fmap f x = pure f <*> x",
|
||||
T::fmap(f, fa0()),
|
||||
T::seq(T::pure(f), fa0()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn discard_can_be_expressed_via_seq_or_la2<
|
||||
'a,
|
||||
T: Applicative + Eqr,
|
||||
A: 'a,
|
||||
B: 'a + Debug + PartialEq,
|
||||
FA0: Fn() -> T::F<'a, A>,
|
||||
FB0: Fn() -> T::F<'a, B>,
|
||||
>(
|
||||
fa0: FA0,
|
||||
fb0: FB0,
|
||||
) -> R {
|
||||
T::eqr(
|
||||
"discard via seq: u *> v = (id <$ u) <*> v",
|
||||
T::discard_first(fa0(), fb0()),
|
||||
T::seq(T::replace(fa0(), |b| b), fb0()),
|
||||
) + T::eqr(
|
||||
"discard via la2: u <* v = liftA2 const u v",
|
||||
T::discard_second(fb0(), fa0()),
|
||||
T::la2(|b, _| b, fb0(), fa0()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn bind_respects_left_identity<
|
||||
'a,
|
||||
T: Monad + Eqr,
|
||||
A: 'a,
|
||||
B: 'a + Debug + PartialEq,
|
||||
F: 'a + Fn(A) -> T::F<'a, B>,
|
||||
A0: Fn() -> A,
|
||||
>(
|
||||
f: F,
|
||||
a0: A0,
|
||||
) -> R {
|
||||
T::eqr(
|
||||
"left identity: pure a >>= k = k a",
|
||||
f(a0()),
|
||||
T::bind(T::pure(a0()), f),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn bind_respects_right_identity<
|
||||
'a,
|
||||
T: 'a + Monad + Eqr,
|
||||
A: 'a + Debug + PartialEq,
|
||||
FA0: Fn() -> T::F<'a, A>,
|
||||
>(
|
||||
fa0: FA0,
|
||||
) -> R {
|
||||
T::eqr(
|
||||
"right identity: m >>= bind = m",
|
||||
T::bind(fa0(), T::pure),
|
||||
fa0(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn bind_is_associative<
|
||||
'a,
|
||||
T: Monad + Eqr,
|
||||
A: 'a,
|
||||
B: 'a,
|
||||
C: 'a + Debug + PartialEq,
|
||||
F: 'a + Clone + Fn(B) -> T::F<'a, C>,
|
||||
G: 'a + Clone + Fn(A) -> T::F<'a, B>,
|
||||
FA0: Fn() -> T::F<'a, A>,
|
||||
>(
|
||||
f: F,
|
||||
g: G,
|
||||
fa0: FA0,
|
||||
) -> R {
|
||||
T::eqr(
|
||||
r"associativity: m >>= (\x -> k x >>= h) = (m >>= k) >>= h",
|
||||
T::bind(T::bind(fa0(), g.clone()), f.clone()),
|
||||
T::bind(fa0(), move |a| T::bind(g(a), f)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn seq_can_be_expressed_via_bind<
|
||||
'a,
|
||||
T: Monad + Eqr,
|
||||
A: 'a,
|
||||
B: 'a + Debug + PartialEq,
|
||||
F: 'a + Fn(A) -> B,
|
||||
FA0: 'a + Fn() -> T::F<'a, A>,
|
||||
FF0: Fn() -> T::F<'a, F>,
|
||||
>(
|
||||
ff0: FF0,
|
||||
fa0: FA0,
|
||||
) -> R {
|
||||
T::eqr(
|
||||
r"seq via bind: m1 <*> m2 = m1 >>= (\x1 -> m2 >>= (\x2 -> pure (x1 x2)))",
|
||||
T::seq(ff0(), fa0()),
|
||||
T::bind(ff0(), move |f| T::bind(fa0(), move |a| T::pure(f(a)))),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn fmap_can_be_expressed_via_bind<
|
||||
'a,
|
||||
T: Monad + Eqr,
|
||||
A: 'a,
|
||||
B: 'a + Debug + PartialEq,
|
||||
F: 'a + Copy + Fn(A) -> B,
|
||||
FA0: 'a + Fn() -> T::F<'a, A>,
|
||||
>(
|
||||
f: F,
|
||||
fa0: FA0,
|
||||
) -> R {
|
||||
T::eqr(
|
||||
"fmap via bind: fmap f xs = xs >>= return . f",
|
||||
T::fmap(f, fa0()),
|
||||
T::bind(fa0(), move |a| T::pure(f(a))),
|
||||
)
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
pub mod copy_func;
|
||||
pub mod core;
|
||||
pub mod func;
|
||||
pub mod optionclass;
|
||||
pub mod copy_func;
|
||||
|
Loading…
Reference in New Issue
Block a user