#[fp_macros::document_module]
mod inner {
use {
crate::{
brands::RcCoyonedaBrand,
classes::{
Lift,
NaturalTransformation,
*,
},
impl_kind,
kinds::*,
},
fp_macros::*,
std::rc::Rc,
};
#[document_type_parameters(
"The lifetime of the values.",
"The brand of the underlying type constructor.",
"The output type of the accumulated mapping function."
)]
#[document_parameters("The trait object reference.")]
trait RcCoyonedaLowerRef<'a, F, A: 'a>: 'a
where
F: Kind_cdc7cd43dac7585f + 'a, {
#[document_signature]
#[document_returns("The underlying functor value with accumulated functions applied.")]
#[document_examples]
fn lower_ref(&self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
where
F: Functor;
}
struct RcCoyonedaBase<'a, F, A: 'a>
where
F: Kind_cdc7cd43dac7585f + 'a, {
fa: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
}
#[document_type_parameters(
"The lifetime of the values.",
"The brand of the underlying type constructor.",
"The type of the value inside the functor."
)]
#[document_parameters("The base layer instance.")]
impl<'a, F, A: 'a> RcCoyonedaLowerRef<'a, F, A> for RcCoyonedaBase<'a, F, A>
where
F: Kind_cdc7cd43dac7585f + 'a,
Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone,
{
#[document_signature]
#[document_returns("A clone of the underlying functor value.")]
#[document_examples]
fn lower_ref(&self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
where
F: Functor, {
self.fa.clone()
}
}
struct RcCoyonedaMapLayer<'a, F, B: 'a, A: 'a>
where
F: Kind_cdc7cd43dac7585f + 'a, {
inner: Rc<dyn RcCoyonedaLowerRef<'a, F, B> + 'a>,
func: Rc<dyn Fn(B) -> A + 'a>,
}
#[document_type_parameters(
"The lifetime of the values.",
"The brand of the underlying type constructor.",
"The input type of this layer's mapping function.",
"The output type of this layer's mapping function."
)]
#[document_parameters("The map layer instance.")]
impl<'a, F, B: 'a, A: 'a> RcCoyonedaLowerRef<'a, F, A> for RcCoyonedaMapLayer<'a, F, B, A>
where
F: Kind_cdc7cd43dac7585f + 'a,
{
#[document_signature]
#[document_returns("The underlying functor value with this layer's function applied.")]
#[document_examples]
fn lower_ref(&self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
where
F: Functor, {
#[cfg(feature = "stacker")]
{
stacker::maybe_grow(32 * 1024, 1024 * 1024, || {
let lowered = self.inner.lower_ref();
let func = self.func.clone();
F::map(move |b| (*func)(b), lowered)
})
}
#[cfg(not(feature = "stacker"))]
{
let lowered = self.inner.lower_ref();
let func = self.func.clone();
F::map(move |b| (*func)(b), lowered)
}
}
}
struct RcCoyonedaNewLayer<'a, F, B: 'a, A: 'a>
where
F: Kind_cdc7cd43dac7585f + 'a, {
fb: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
func: Rc<dyn Fn(B) -> A + 'a>,
}
#[document_type_parameters(
"The lifetime of the values.",
"The brand of the underlying type constructor.",
"The input type of the stored function.",
"The output type of the stored function."
)]
#[document_parameters("The new layer instance.")]
impl<'a, F, B: 'a, A: 'a> RcCoyonedaLowerRef<'a, F, A> for RcCoyonedaNewLayer<'a, F, B, A>
where
F: Kind_cdc7cd43dac7585f + 'a,
Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>): Clone,
{
#[document_signature]
#[document_returns("The underlying functor value with the stored function applied.")]
#[document_examples]
fn lower_ref(&self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
where
F: Functor, {
let func = self.func.clone();
F::map(move |b| (*func)(b), self.fb.clone())
}
}
#[document_type_parameters(
"The lifetime of the values.",
"The brand of the underlying type constructor.",
"The current output type."
)]
pub struct RcCoyoneda<'a, F, A: 'a>(Rc<dyn RcCoyonedaLowerRef<'a, F, A> + 'a>)
where
F: Kind_cdc7cd43dac7585f + 'a;
#[document_type_parameters(
"The lifetime of the values.",
"The brand of the underlying type constructor.",
"The current output type."
)]
#[document_parameters("The `RcCoyoneda` instance to clone.")]
impl<'a, F, A: 'a> Clone for RcCoyoneda<'a, F, A>
where
F: Kind_cdc7cd43dac7585f + 'a,
{
#[document_signature]
#[document_returns("A new `RcCoyoneda` sharing the same inner layers.")]
#[document_examples]
fn clone(&self) -> Self {
RcCoyoneda(self.0.clone())
}
}
#[document_type_parameters(
"The lifetime of the values.",
"The brand of the underlying type constructor.",
"The current output type."
)]
#[document_parameters("The `RcCoyoneda` instance.")]
impl<'a, F, A: 'a> RcCoyoneda<'a, F, A>
where
F: Kind_cdc7cd43dac7585f + 'a,
{
#[document_signature]
#[document_parameters("The functor value to lift.")]
#[document_returns("An `RcCoyoneda` wrapping the value.")]
#[document_examples]
pub fn lift(fa: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)) -> Self
where
Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone, {
RcCoyoneda(Rc::new(RcCoyonedaBase {
fa,
}))
}
#[document_signature]
#[document_returns("The underlying functor value with all accumulated functions applied.")]
#[document_examples]
pub fn lower_ref(&self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
where
F: Functor, {
self.0.lower_ref()
}
#[document_signature]
#[document_returns("A new `RcCoyoneda` with a single base layer.")]
#[document_examples]
pub fn collapse(&self) -> RcCoyoneda<'a, F, A>
where
F: Functor,
Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone, {
RcCoyoneda::lift(self.lower_ref())
}
#[document_signature]
#[document_type_parameters(
"The brand of the cloneable function to use.",
"The type of the monoid."
)]
#[document_parameters("The function to map each element to a monoid.")]
#[document_returns("The combined monoid value.")]
#[document_examples]
pub fn fold_map<FnBrand: LiftFn + 'a, M>(
&self,
func: impl Fn(A) -> M + 'a,
) -> M
where
F: Functor + Foldable,
A: Clone,
M: Monoid + 'a, {
F::fold_map::<FnBrand, A, M>(func, self.lower_ref())
}
#[document_signature]
#[document_type_parameters("The new output type after applying the function.")]
#[document_parameters("The function to apply.")]
#[document_returns("A new `RcCoyoneda` with the function stored for deferred application.")]
#[document_examples]
pub fn map<B: 'a>(
self,
f: impl Fn(A) -> B + 'a,
) -> RcCoyoneda<'a, F, B> {
RcCoyoneda(Rc::new(RcCoyonedaMapLayer {
inner: self.0,
func: Rc::new(f),
}))
}
#[document_signature]
#[document_type_parameters("The input type of the function.")]
#[document_parameters("The function to defer.", "The functor value.")]
#[document_returns("An `RcCoyoneda` wrapping the value with the deferred function.")]
#[document_examples]
pub fn new<B: 'a>(
f: impl Fn(B) -> A + 'a,
fb: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
) -> Self
where
Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>): Clone, {
RcCoyoneda(Rc::new(RcCoyonedaNewLayer {
fb,
func: Rc::new(f),
}))
}
#[document_signature]
#[document_type_parameters("The target functor brand.")]
#[document_parameters("The natural transformation from `F` to `G`.")]
#[document_returns("A new `RcCoyoneda` over the target functor `G`.")]
#[document_examples]
pub fn hoist<G: Kind_cdc7cd43dac7585f + 'a>(
self,
nat: impl NaturalTransformation<F, G>,
) -> RcCoyoneda<'a, G, A>
where
F: Functor,
Apply!(<G as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone, {
RcCoyoneda::lift(nat.transform(self.lower_ref()))
}
#[document_signature]
#[document_parameters("The value to wrap.")]
#[document_returns("An `RcCoyoneda` containing the pure value.")]
#[document_examples]
pub fn pure(a: A) -> Self
where
F: Pointed,
Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone, {
RcCoyoneda::lift(F::pure(a))
}
#[document_signature]
#[document_type_parameters("The output type of the bound computation.")]
#[document_parameters(
"The function to apply to the inner value, returning a new `RcCoyoneda`."
)]
#[document_returns("A new `RcCoyoneda` containing the bound result.")]
#[document_examples]
pub fn bind<B: 'a>(
self,
func: impl Fn(A) -> RcCoyoneda<'a, F, B> + 'a,
) -> RcCoyoneda<'a, F, B>
where
F: Functor + Semimonad,
Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>): Clone, {
RcCoyoneda::lift(F::bind(self.lower_ref(), move |a| func(a).lower_ref()))
}
#[document_signature]
#[document_type_parameters(
"The brand of the cloneable function wrapper.",
"The type of the function input.",
"The type of the function output."
)]
#[document_parameters(
"The `RcCoyoneda` containing the function(s).",
"The `RcCoyoneda` containing the value(s)."
)]
#[document_returns("A new `RcCoyoneda` containing the applied result(s).")]
#[document_examples]
pub fn apply<FnBrand: LiftFn + 'a, B: Clone + 'a, C: 'a>(
ff: RcCoyoneda<'a, F, <FnBrand as CloneFn>::Of<'a, B, C>>,
fa: RcCoyoneda<'a, F, B>,
) -> RcCoyoneda<'a, F, C>
where
F: Functor + Semiapplicative,
Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>): Clone, {
RcCoyoneda::lift(F::apply::<FnBrand, B, C>(ff.lower_ref(), fa.lower_ref()))
}
#[document_signature]
#[document_type_parameters("The type of the second value.", "The type of the result.")]
#[document_parameters("The binary function to apply.", "The second `RcCoyoneda` value.")]
#[document_returns("An `RcCoyoneda` containing the result.")]
#[document_examples]
pub fn lift2<B: Clone + 'a, C: 'a>(
self,
func: impl Fn(A, B) -> C + 'a,
fb: RcCoyoneda<'a, F, B>,
) -> RcCoyoneda<'a, F, C>
where
F: Functor + Lift,
A: Clone,
Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>): Clone, {
RcCoyoneda::lift(F::lift2(func, self.lower_ref(), fb.lower_ref()))
}
}
impl_kind! {
impl<F: Kind_cdc7cd43dac7585f + 'static> for RcCoyonedaBrand<F> {
type Of<'a, A: 'a>: 'a = RcCoyoneda<'a, F, A>;
}
}
#[document_type_parameters("The brand of the underlying type constructor.")]
impl<F: Kind_cdc7cd43dac7585f + 'static> Functor for RcCoyonedaBrand<F> {
#[document_signature]
#[document_type_parameters(
"The lifetime of the values.",
"The type of the current output.",
"The type of the new output."
)]
#[document_parameters("The function to apply.", "The `RcCoyoneda` value.")]
#[document_returns("A new `RcCoyoneda` with the function stored for deferred application.")]
#[document_examples]
fn map<'a, A: 'a, B: 'a>(
func: impl Fn(A) -> B + 'a,
fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
fa.map(func)
}
}
#[document_type_parameters("The brand of the underlying foldable functor.")]
impl<F: Functor + Foldable + 'static> Foldable for RcCoyonedaBrand<F> {
#[document_signature]
#[document_type_parameters(
"The lifetime of the elements.",
"The brand of the cloneable function to use.",
"The type of the elements in the structure.",
"The type of the monoid."
)]
#[document_parameters(
"The function to map each element to a monoid.",
"The `RcCoyoneda` structure to fold."
)]
#[document_returns("The combined monoid value.")]
#[document_examples]
fn fold_map<'a, FnBrand, A: 'a + Clone, M>(
func: impl Fn(A) -> M + 'a,
fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
) -> M
where
M: Monoid + 'a,
FnBrand: LiftFn + 'a, {
F::fold_map::<FnBrand, A, M>(func, fa.lower_ref())
}
}
#[document_type_parameters(
"The lifetime of the values.",
"The brand of the underlying type constructor.",
"The current output type."
)]
#[document_parameters("The `RcCoyoneda` instance.")]
impl<'a, F, A: 'a> core::fmt::Debug for RcCoyoneda<'a, F, A>
where
F: Kind_cdc7cd43dac7585f + 'a,
{
#[document_signature]
#[document_parameters("The formatter.")]
#[document_returns("The formatting result.")]
#[document_examples]
fn fmt(
&self,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
f.write_str("RcCoyoneda(<opaque>)")
}
}
#[document_type_parameters(
"The lifetime of the values.",
"The brand of the underlying functor.",
"The type of the values."
)]
impl<'a, F, A: 'a> From<RcCoyoneda<'a, F, A>> for crate::types::Coyoneda<'a, F, A>
where
F: Kind_cdc7cd43dac7585f + Functor + 'a,
{
#[document_signature]
#[document_parameters("The `RcCoyoneda` to convert.")]
#[document_returns("A `Coyoneda` containing the lowered value.")]
#[document_examples]
fn from(rc: RcCoyoneda<'a, F, A>) -> Self {
crate::types::Coyoneda::lift(rc.lower_ref())
}
}
}
pub use inner::*;
#[cfg(test)]
mod tests {
use crate::{
brands::*,
functions::*,
types::*,
};
#[test]
fn lift_lower_ref_identity_option() {
let coyo = RcCoyoneda::<OptionBrand, _>::lift(Some(42));
assert_eq!(coyo.lower_ref(), Some(42));
}
#[test]
fn lift_lower_ref_identity_vec() {
let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
assert_eq!(coyo.lower_ref(), vec![1, 2, 3]);
}
#[test]
fn clone_and_lower_ref() {
let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x + 1);
let coyo2 = coyo.clone();
assert_eq!(coyo.lower_ref(), vec![2, 3, 4]);
assert_eq!(coyo2.lower_ref(), vec![2, 3, 4]);
}
#[test]
fn chained_maps() {
let result = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3])
.map(|x| x + 1)
.map(|x| x * 2)
.map(|x| x.to_string())
.lower_ref();
assert_eq!(result, vec!["4", "6", "8"]);
}
#[test]
fn functor_identity_law() {
let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
let result =
explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(identity, coyo).lower_ref();
assert_eq!(result, vec![1, 2, 3]);
}
#[test]
fn functor_composition_law() {
let f = |x: i32| x + 1;
let g = |x: i32| x * 2;
let coyo1 = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
let left = explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(compose(f, g), coyo1)
.lower_ref();
let coyo2 = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
let right = explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(
f,
explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(g, coyo2),
)
.lower_ref();
assert_eq!(left, right);
}
#[test]
fn fold_map_on_mapped() {
let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x * 10);
let result = explicit::fold_map::<RcFnBrand, RcCoyonedaBrand<VecBrand>, _, _, _, _>(
|x: i32| x.to_string(),
coyo,
);
assert_eq!(result, "102030".to_string());
}
#[test]
fn lower_ref_multiple_times() {
let coyo = RcCoyoneda::<OptionBrand, _>::lift(Some(5)).map(|x| x + 1);
assert_eq!(coyo.lower_ref(), Some(6));
assert_eq!(coyo.lower_ref(), Some(6));
assert_eq!(coyo.lower_ref(), Some(6));
}
#[test]
fn map_on_none_stays_none() {
let result = RcCoyoneda::<OptionBrand, _>::lift(None::<i32>)
.map(|x| x + 1)
.map(|x| x * 2)
.lower_ref();
assert_eq!(result, None);
}
mod property {
use {
crate::{
brands::*,
functions::*,
types::*,
},
quickcheck_macros::quickcheck,
};
#[quickcheck]
fn functor_identity_vec(v: Vec<i32>) -> bool {
let coyo = RcCoyoneda::<VecBrand, _>::lift(v.clone());
explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(identity, coyo).lower_ref() == v
}
#[quickcheck]
fn functor_identity_option(x: Option<i32>) -> bool {
let coyo = RcCoyoneda::<OptionBrand, _>::lift(x);
explicit::map::<RcCoyonedaBrand<OptionBrand>, _, _, _, _>(identity, coyo).lower_ref()
== x
}
#[quickcheck]
fn functor_composition_vec(v: Vec<i32>) -> bool {
let f = |x: i32| x.wrapping_add(1);
let g = |x: i32| x.wrapping_mul(2);
let left = explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(
compose(f, g),
RcCoyoneda::<VecBrand, _>::lift(v.clone()),
)
.lower_ref();
let right = explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(
f,
explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(
g,
RcCoyoneda::<VecBrand, _>::lift(v),
),
)
.lower_ref();
left == right
}
#[quickcheck]
fn functor_composition_option(x: Option<i32>) -> bool {
let f = |x: i32| x.wrapping_add(1);
let g = |x: i32| x.wrapping_mul(2);
let left = explicit::map::<RcCoyonedaBrand<OptionBrand>, _, _, _, _>(
compose(f, g),
RcCoyoneda::<OptionBrand, _>::lift(x),
)
.lower_ref();
let right = explicit::map::<RcCoyonedaBrand<OptionBrand>, _, _, _, _>(
f,
explicit::map::<RcCoyonedaBrand<OptionBrand>, _, _, _, _>(
g,
RcCoyoneda::<OptionBrand, _>::lift(x),
),
)
.lower_ref();
left == right
}
#[quickcheck]
fn foldable_consistency_vec(v: Vec<i32>) -> bool {
let coyo = RcCoyoneda::<VecBrand, _>::lift(v.clone()).map(|x: i32| x.wrapping_add(1));
let via_coyoneda: String =
explicit::fold_map::<RcFnBrand, RcCoyonedaBrand<VecBrand>, _, _, _, _>(
|x: i32| x.to_string(),
coyo,
);
let direct: String = explicit::fold_map::<RcFnBrand, VecBrand, _, _, _, _>(
|x: i32| x.to_string(),
v.iter().map(|x| x.wrapping_add(1)).collect::<Vec<_>>(),
);
via_coyoneda == direct
}
#[quickcheck]
fn collapse_preserves_value(v: Vec<i32>) -> bool {
let coyo = RcCoyoneda::<VecBrand, _>::lift(v)
.map(|x: i32| x.wrapping_add(1))
.map(|x: i32| x.wrapping_mul(2));
let before = coyo.lower_ref();
let after = coyo.collapse().lower_ref();
before == after
}
}
}