use crate::typeclasses::hkt::{FunctorHKT, HKT};
#[cfg(feature = "alloc")]
use super::cofree::Cofree;
#[cfg(feature = "alloc")]
use super::free::{Aut, Free};
#[cfg(feature = "alloc")]
use super::traits::{Corecursiva, FunctorBasis, Recursiva};
#[cfg(feature = "alloc")]
#[inline]
pub fn cata<T, A, Alg>(alg: Alg, t: T) -> A
where
T: Recursiva,
Alg: Fn(<T::Base as HKT>::Target<A>) -> A + Clone,
{
cata_impl(&alg, t)
}
#[cfg(feature = "alloc")]
#[inline]
fn cata_impl<T, A, Alg>(alg: &Alg, t: T) -> A
where
T: Recursiva,
Alg: Fn(<T::Base as HKT>::Target<A>) -> A,
{
let layer = t.project();
let mapped = T::Base::map(layer, |sub| cata_impl(alg, sub));
alg(mapped)
}
#[cfg(feature = "alloc")]
#[inline]
pub fn ana<T, A, Coalg>(coalg: Coalg, seed: A) -> T
where
T: Corecursiva,
Coalg: Fn(A) -> <T::Base as HKT>::Target<A> + Clone,
{
ana_impl(&coalg, seed)
}
#[cfg(feature = "alloc")]
#[inline]
fn ana_impl<T, A, Coalg>(coalg: &Coalg, seed: A) -> T
where
T: Corecursiva,
Coalg: Fn(A) -> <T::Base as HKT>::Target<A>,
{
let layer = coalg(seed);
let mapped = T::Base::map(layer, |sub| ana_impl(coalg, sub));
T::embed(mapped)
}
#[cfg(feature = "alloc")]
#[inline]
pub fn hylo<F, A, B, Alg, Coalg>(alg: Alg, coalg: Coalg, seed: A) -> B
where
F: FunctorHKT,
Alg: Fn(F::Target<B>) -> B + Clone,
Coalg: Fn(A) -> F::Target<A> + Clone,
{
hylo_impl::<F, A, B, Alg, Coalg>(&alg, &coalg, seed)
}
#[cfg(feature = "alloc")]
#[inline]
fn hylo_impl<F, A, B, Alg, Coalg>(alg: &Alg, coalg: &Coalg, seed: A) -> B
where
F: FunctorHKT,
Alg: Fn(F::Target<B>) -> B,
Coalg: Fn(A) -> F::Target<A>,
{
let layer = coalg(seed);
let mapped = F::map(layer, |sub| {
hylo_impl::<F, A, B, Alg, Coalg>(alg, coalg, sub)
});
alg(mapped)
}
#[cfg(feature = "alloc")]
#[inline]
pub fn para<T, A, Alg>(alg: Alg, t: T) -> A
where
T: Recursiva + Clone,
Alg: Fn(<T::Base as HKT>::Target<(T, A)>) -> A + Clone,
{
para_impl(&alg, t)
}
#[cfg(feature = "alloc")]
#[inline]
fn para_impl<T, A, Alg>(alg: &Alg, t: T) -> A
where
T: Recursiva + Clone,
Alg: Fn(<T::Base as HKT>::Target<(T, A)>) -> A,
{
let layer = t.project();
let mapped = T::Base::map(layer, |sub: T| {
let sub_clone = sub.clone();
let result = para_impl(alg, sub);
(sub_clone, result)
});
alg(mapped)
}
#[cfg(feature = "alloc")]
#[inline]
pub fn apo<T, A, Coalg>(coalg: Coalg, seed: A) -> T
where
T: Corecursiva + Clone,
Coalg: Fn(A) -> <T::Base as HKT>::Target<Aut<T, A>> + Clone,
{
apo_impl(&coalg, seed)
}
#[cfg(feature = "alloc")]
#[inline]
fn apo_impl<T, A, Coalg>(coalg: &Coalg, seed: A) -> T
where
T: Corecursiva + Clone,
Coalg: Fn(A) -> <T::Base as HKT>::Target<Aut<T, A>>,
{
let layer = coalg(seed);
let mapped = T::Base::map(layer, |either| match either {
Aut::Sinister(t) => t,
Aut::Dexter(a) => apo_impl(coalg, a),
});
T::embed(mapped)
}
#[cfg(feature = "alloc")]
#[inline]
pub fn histo<T, A, Alg>(alg: Alg, t: T) -> A
where
T: Recursiva,
Alg: Fn(<T::Base as HKT>::Target<Cofree<T::Base, A>>) -> A + Clone,
<T::Base as HKT>::Target<Cofree<T::Base, A>>: Clone,
{
let cofree = histo_build(&alg, t);
cofree.attribute
}
#[cfg(feature = "alloc")]
#[inline]
fn histo_build<T, A, Alg>(alg: &Alg, t: T) -> Cofree<T::Base, A>
where
T: Recursiva,
Alg: Fn(<T::Base as HKT>::Target<Cofree<T::Base, A>>) -> A,
<T::Base as HKT>::Target<Cofree<T::Base, A>>: Clone,
{
let layer = t.project();
let children = T::Base::map(layer, |sub| histo_build(alg, sub));
let attr = alg(children.clone());
Cofree::new(attr, children)
}
#[cfg(feature = "alloc")]
#[inline]
pub fn futu<T, A, Coalg>(coalg: Coalg, seed: A) -> T
where
T: Corecursiva,
Coalg: Fn(A) -> <T::Base as HKT>::Target<Free<T::Base, A>> + Clone,
{
futu_impl(&coalg, seed)
}
#[cfg(feature = "alloc")]
#[inline]
fn futu_impl<T, A, Coalg>(coalg: &Coalg, seed: A) -> T
where
T: Corecursiva,
Coalg: Fn(A) -> <T::Base as HKT>::Target<Free<T::Base, A>>,
{
let layer = coalg(seed);
let mapped = T::Base::map(layer, |free| free_to_t(coalg, free));
T::embed(mapped)
}
#[cfg(feature = "alloc")]
#[inline]
fn free_to_t<T, A, Coalg>(coalg: &Coalg, free: Free<T::Base, A>) -> T
where
T: Corecursiva,
Coalg: Fn(A) -> <T::Base as HKT>::Target<Free<T::Base, A>>,
{
match free {
Free::Purus(a) => futu_impl(coalg, a),
Free::Suspensus(layer) => {
let mapped = T::Base::map(*layer, |inner_free| free_to_t(coalg, inner_free));
T::embed(mapped)
}
}
}
#[cfg(feature = "alloc")]
#[inline]
pub fn zygo<T, A, B, AuxAlg, Alg>(aux_alg: AuxAlg, alg: Alg, t: T) -> A
where
T: Recursiva,
AuxAlg: Fn(<T::Base as HKT>::Target<B>) -> B + Clone,
Alg: Fn(<T::Base as HKT>::Target<(B, A)>) -> A + Clone,
<T::Base as HKT>::Target<(B, A)>: Clone,
{
let (_, result) = zygo_impl(&aux_alg, &alg, t);
result
}
#[cfg(feature = "alloc")]
#[inline]
fn zygo_impl<T, A, B, AuxAlg, Alg>(aux_alg: &AuxAlg, alg: &Alg, t: T) -> (B, A)
where
T: Recursiva,
AuxAlg: Fn(<T::Base as HKT>::Target<B>) -> B,
Alg: Fn(<T::Base as HKT>::Target<(B, A)>) -> A,
<T::Base as HKT>::Target<(B, A)>: Clone,
{
let layer = t.project();
let mapped = T::Base::map(layer, |sub| zygo_impl(aux_alg, alg, sub));
let aux_layer = T::Base::map(mapped.clone(), |(b, _)| b);
let main_layer = mapped;
let aux_result = aux_alg(aux_layer);
let main_result = alg(main_layer);
(aux_result, main_result)
}
#[cfg(feature = "alloc")]
#[inline]
pub fn chrono<F, A, B, Alg, Coalg>(alg: Alg, coalg: Coalg, seed: A) -> B
where
F: FunctorHKT,
Alg: Fn(F::Target<Cofree<F, B>>) -> B + Clone,
Coalg: Fn(A) -> F::Target<Free<F, A>> + Clone,
F::Target<Cofree<F, B>>: Clone,
{
let cofree = chrono_build::<F, A, B, Alg, Coalg>(&alg, &coalg, seed);
cofree.attribute
}
#[cfg(feature = "alloc")]
#[inline]
fn chrono_build<F, A, B, Alg, Coalg>(alg: &Alg, coalg: &Coalg, seed: A) -> Cofree<F, B>
where
F: FunctorHKT,
Alg: Fn(F::Target<Cofree<F, B>>) -> B,
Coalg: Fn(A) -> F::Target<Free<F, A>>,
F::Target<Cofree<F, B>>: Clone,
{
let layer = coalg(seed);
let children = F::map(layer, |free| {
chrono_free::<F, A, B, Alg, Coalg>(alg, coalg, free)
});
let attr = alg(children.clone());
Cofree::new(attr, children)
}
#[cfg(feature = "alloc")]
#[inline]
fn chrono_free<F, A, B, Alg, Coalg>(alg: &Alg, coalg: &Coalg, free: Free<F, A>) -> Cofree<F, B>
where
F: FunctorHKT,
Alg: Fn(F::Target<Cofree<F, B>>) -> B,
Coalg: Fn(A) -> F::Target<Free<F, A>>,
F::Target<Cofree<F, B>>: Clone,
{
match free {
Free::Purus(a) => chrono_build(alg, coalg, a),
Free::Suspensus(layer) => {
let children = F::map(*layer, |inner| chrono_free(alg, coalg, inner));
let attr = alg(children.clone());
Cofree::new(attr, children)
}
}
}
#[cfg(feature = "alloc")]
#[inline]
pub fn dyna<F, A, B, Alg, Coalg>(alg: Alg, coalg: Coalg, seed: A) -> B
where
F: FunctorHKT,
Alg: Fn(F::Target<Cofree<F, B>>) -> B + Clone,
Coalg: Fn(A) -> F::Target<A> + Clone,
F::Target<Cofree<F, B>>: Clone,
{
let cofree = dyna_build::<F, A, B, Alg, Coalg>(&alg, &coalg, seed);
cofree.attribute
}
#[cfg(feature = "alloc")]
#[inline]
fn dyna_build<F, A, B, Alg, Coalg>(alg: &Alg, coalg: &Coalg, seed: A) -> Cofree<F, B>
where
F: FunctorHKT,
Alg: Fn(F::Target<Cofree<F, B>>) -> B,
Coalg: Fn(A) -> F::Target<A>,
F::Target<Cofree<F, B>>: Clone,
{
let layer = coalg(seed);
let children = F::map(layer, |sub| dyna_build(alg, coalg, sub));
let attr = alg(children.clone());
Cofree::new(attr, children)
}
#[cfg(feature = "alloc")]
#[inline]
pub fn mcata<T, A, Alg>(alg: Alg, t: T) -> A
where
T: Recursiva,
Alg: for<'a> Fn(&'a dyn Fn(T) -> A, <T::Base as HKT>::Target<T>) -> A + Clone,
{
mcata_impl(&alg, t)
}
#[cfg(feature = "alloc")]
#[inline]
fn mcata_impl<T, A, Alg>(alg: &Alg, t: T) -> A
where
T: Recursiva,
Alg: for<'a> Fn(&'a dyn Fn(T) -> A, <T::Base as HKT>::Target<T>) -> A,
{
let layer = t.project();
let recurse: &dyn Fn(T) -> A = &|sub| mcata_impl(alg, sub);
alg(recurse, layer)
}
#[cfg(feature = "alloc")]
#[inline]
pub fn mana<T, A, Coalg>(coalg: Coalg, seed: A) -> T
where
T: Corecursiva,
Coalg: for<'a> Fn(&'a dyn Fn(A) -> T, A) -> <T::Base as HKT>::Target<T> + Clone,
{
mana_impl(&coalg, seed)
}
#[cfg(feature = "alloc")]
#[inline]
fn mana_impl<T, A, Coalg>(coalg: &Coalg, seed: A) -> T
where
T: Corecursiva,
Coalg: for<'a> Fn(&'a dyn Fn(A) -> T, A) -> <T::Base as HKT>::Target<T>,
{
let recurse: &dyn Fn(A) -> T = &|sub| mana_impl(coalg, sub);
let layer = coalg(recurse, seed);
T::embed(layer)
}
#[cfg(feature = "alloc")]
#[inline]
pub fn mhylo<F, A, B, Alg, Coalg>(alg: Alg, coalg: Coalg, seed: A) -> B
where
F: FunctorHKT,
Alg: for<'a> Fn(&'a dyn Fn(A) -> B, F::Target<A>) -> B + Clone,
Coalg: Fn(A) -> F::Target<A> + Clone,
{
mhylo_impl::<F, A, B, Alg, Coalg>(&alg, &coalg, seed)
}
#[cfg(feature = "alloc")]
#[inline]
fn mhylo_impl<F, A, B, Alg, Coalg>(alg: &Alg, coalg: &Coalg, seed: A) -> B
where
F: FunctorHKT,
Alg: for<'a> Fn(&'a dyn Fn(A) -> B, F::Target<A>) -> B,
Coalg: Fn(A) -> F::Target<A>,
{
let layer = coalg(seed);
let recurse: &dyn Fn(A) -> B = &|sub| mhylo_impl::<F, A, B, Alg, Coalg>(alg, coalg, sub);
alg(recurse, layer)
}
#[cfg(feature = "alloc")]
pub type Algebra<F, A> = dyn Fn(<F as HKT>::Target<A>) -> A;
#[cfg(feature = "alloc")]
pub type Coalgebra<F, A> = dyn Fn(A) -> <F as HKT>::Target<A>;
#[cfg(feature = "alloc")]
pub type RAlgebra<T, A> = dyn Fn(<<T as FunctorBasis>::Base as HKT>::Target<(T, A)>) -> A;
#[cfg(feature = "alloc")]
pub type RCoalgebra<T, A> = dyn Fn(A) -> <<T as FunctorBasis>::Base as HKT>::Target<Aut<T, A>>;