#[fp_macros::document_module]
mod inner {
use {
crate::{
brands::ArcCoyonedaBrand,
classes::{
Lift,
NaturalTransformation,
*,
},
impl_kind,
kinds::*,
},
fp_macros::*,
std::sync::Arc,
};
#[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 ArcCoyonedaLowerRef<'a, F, A: 'a>: Send + Sync + '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 ArcCoyonedaBase<'a, F, A: 'a>
where
F: Kind_cdc7cd43dac7585f<Of<'a, A>: Send + Sync> + '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> ArcCoyonedaLowerRef<'a, F, A> for ArcCoyonedaBase<'a, F, A>
where
F: Kind_cdc7cd43dac7585f + 'a,
Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone + Send + Sync,
{
#[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 ArcCoyonedaMapLayer<'a, F, B: 'a, A: 'a>
where
F: Kind_cdc7cd43dac7585f + 'a, {
inner: Arc<dyn ArcCoyonedaLowerRef<'a, F, B> + 'a>,
func: Arc<dyn Fn(B) -> A + Send + Sync + '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> ArcCoyonedaLowerRef<'a, F, A> for ArcCoyonedaMapLayer<'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 ArcCoyonedaNewLayer<'a, F, B: 'a, A: 'a>
where
F: Kind_cdc7cd43dac7585f<Of<'a, B>: Send + Sync> + 'a, {
fb: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
func: Arc<dyn Fn(B) -> A + Send + Sync + '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> ArcCoyonedaLowerRef<'a, F, A> for ArcCoyonedaNewLayer<'a, F, B, A>
where
F: Kind_cdc7cd43dac7585f + 'a,
Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>): Clone + Send + Sync,
{
#[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 ArcCoyoneda<'a, F, A: 'a>(Arc<dyn ArcCoyonedaLowerRef<'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 `ArcCoyoneda` instance to clone.")]
impl<'a, F, A: 'a> Clone for ArcCoyoneda<'a, F, A>
where
F: Kind_cdc7cd43dac7585f + 'a,
{
#[document_signature]
#[document_returns("A new `ArcCoyoneda` sharing the same inner layers.")]
#[document_examples]
fn clone(&self) -> Self {
ArcCoyoneda(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 `ArcCoyoneda` instance.")]
impl<'a, F, A: 'a> ArcCoyoneda<'a, F, A>
where
F: Kind_cdc7cd43dac7585f + 'a,
{
#[document_signature]
#[document_parameters("The functor value to lift.")]
#[document_returns("An `ArcCoyoneda` 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 + Send + Sync, {
ArcCoyoneda(Arc::new(ArcCoyonedaBase {
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 `ArcCoyoneda` with a single base layer.")]
#[document_examples]
pub fn collapse(&self) -> ArcCoyoneda<'a, F, A>
where
F: Functor,
Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone + Send + Sync, {
ArcCoyoneda::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 `ArcCoyoneda` with the function stored for deferred application."
)]
#[document_examples]
pub fn map<B: 'a>(
self,
f: impl Fn(A) -> B + Send + Sync + 'a,
) -> ArcCoyoneda<'a, F, B> {
ArcCoyoneda(Arc::new(ArcCoyonedaMapLayer {
inner: self.0,
func: Arc::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 `ArcCoyoneda` wrapping the value with the deferred function.")]
#[document_examples]
pub fn new<B: 'a>(
f: impl Fn(B) -> A + Send + Sync + '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 + Send + Sync, {
ArcCoyoneda(Arc::new(ArcCoyonedaNewLayer {
fb,
func: Arc::new(f),
}))
}
#[document_signature]
#[document_type_parameters("The target functor brand.")]
#[document_parameters("The natural transformation from `F` to `G`.")]
#[document_returns("A new `ArcCoyoneda` over the target functor `G`.")]
#[document_examples]
pub fn hoist<G: Kind_cdc7cd43dac7585f + 'a>(
self,
nat: impl NaturalTransformation<F, G>,
) -> ArcCoyoneda<'a, G, A>
where
F: Functor,
Apply!(<G as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone + Send + Sync, {
ArcCoyoneda::lift(nat.transform(self.lower_ref()))
}
#[document_signature]
#[document_parameters("The value to wrap.")]
#[document_returns("An `ArcCoyoneda` 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 + Send + Sync, {
ArcCoyoneda::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 `ArcCoyoneda`."
)]
#[document_returns("A new `ArcCoyoneda` containing the bound result.")]
#[document_examples]
pub fn bind<B: 'a>(
self,
func: impl Fn(A) -> ArcCoyoneda<'a, F, B> + 'a,
) -> ArcCoyoneda<'a, F, B>
where
F: Functor + Semimonad,
Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>): Clone + Send + Sync, {
ArcCoyoneda::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 `ArcCoyoneda` containing the function(s).",
"The `ArcCoyoneda` containing the value(s)."
)]
#[document_returns("A new `ArcCoyoneda` containing the applied result(s).")]
#[document_examples]
pub fn apply<FnBrand: LiftFn + 'a, B: Clone + 'a, C: 'a>(
ff: ArcCoyoneda<'a, F, <FnBrand as CloneFn>::Of<'a, B, C>>,
fa: ArcCoyoneda<'a, F, B>,
) -> ArcCoyoneda<'a, F, C>
where
F: Functor + Semiapplicative,
Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>): Clone + Send + Sync, {
ArcCoyoneda::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 `ArcCoyoneda` value.")]
#[document_returns("An `ArcCoyoneda` containing the result.")]
#[document_examples]
pub fn lift2<B: Clone + 'a, C: 'a>(
self,
func: impl Fn(A, B) -> C + 'a,
fb: ArcCoyoneda<'a, F, B>,
) -> ArcCoyoneda<'a, F, C>
where
F: Functor + Lift,
A: Clone,
Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>): Clone + Send + Sync, {
ArcCoyoneda::lift(F::lift2(func, self.lower_ref(), fb.lower_ref()))
}
}
impl_kind! {
impl<F: Kind_cdc7cd43dac7585f + 'static> for ArcCoyonedaBrand<F> {
type Of<'a, A: 'a>: 'a = ArcCoyoneda<'a, F, A>;
}
}
#[document_type_parameters("The brand of the underlying foldable functor.")]
impl<F: Functor + Foldable + 'static> Foldable for ArcCoyonedaBrand<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 `ArcCoyoneda` 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 `ArcCoyoneda` instance.")]
impl<'a, F, A: 'a> core::fmt::Debug for ArcCoyoneda<'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("ArcCoyoneda(<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<ArcCoyoneda<'a, F, A>> for crate::types::Coyoneda<'a, F, A>
where
F: Kind_cdc7cd43dac7585f + Functor + 'a,
{
#[document_signature]
#[document_parameters("The `ArcCoyoneda` to convert.")]
#[document_returns("A `Coyoneda` containing the lowered value.")]
#[document_examples]
fn from(arc: ArcCoyoneda<'a, F, A>) -> Self {
crate::types::Coyoneda::lift(arc.lower_ref())
}
}
const _: () = {
fn _assert_send<T: Send>() {}
fn _assert_sync<T: Sync>() {}
fn _check_base<'a, F: Kind_cdc7cd43dac7585f<Of<'a, A>: Send + Sync> + 'a, A: 'a>() {
_assert_send::<ArcCoyonedaBase<'a, F, A>>();
_assert_sync::<ArcCoyonedaBase<'a, F, A>>();
}
fn _check_map_layer<'a, F: Kind_cdc7cd43dac7585f + 'a, B: 'a, A: 'a>() {
_assert_send::<ArcCoyonedaMapLayer<'a, F, B, A>>();
_assert_sync::<ArcCoyonedaMapLayer<'a, F, B, A>>();
}
fn _check_new_layer<
'a,
F: Kind_cdc7cd43dac7585f<Of<'a, B>: Send + Sync> + 'a,
B: 'a,
A: 'a,
>() {
_assert_send::<ArcCoyonedaNewLayer<'a, F, B, A>>();
_assert_sync::<ArcCoyonedaNewLayer<'a, F, B, A>>();
}
};
}
pub use inner::*;
#[cfg(test)]
#[expect(clippy::unwrap_used, reason = "Tests use panicking operations for brevity and clarity")]
mod tests {
use crate::{
brands::*,
functions::*,
types::*,
};
#[test]
fn lift_lower_ref_identity() {
let coyo = ArcCoyoneda::<OptionBrand, _>::lift(Some(42));
assert_eq!(coyo.lower_ref(), Some(42));
}
#[test]
fn chained_maps() {
let result = ArcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3])
.map(|x| x + 1)
.map(|x| x * 2)
.lower_ref();
assert_eq!(result, vec![4, 6, 8]);
}
#[test]
fn clone_and_lower() {
let coyo = ArcCoyoneda::<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 send_across_thread() {
let coyo = ArcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x * 10);
let handle = std::thread::spawn(move || coyo.lower_ref());
assert_eq!(handle.join().unwrap(), vec![10, 20, 30]);
}
#[test]
fn fold_map_on_mapped() {
let coyo = ArcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x * 10);
let result = explicit::fold_map::<RcFnBrand, ArcCoyonedaBrand<VecBrand>, _, _, _, _>(
|x: i32| x.to_string(),
coyo,
);
assert_eq!(result, "102030".to_string());
}
#[test]
fn map_on_none_stays_none() {
let result = ArcCoyoneda::<OptionBrand, _>::lift(None::<i32>).map(|x| x + 1).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 = ArcCoyoneda::<VecBrand, _>::lift(v.clone());
coyo.map(identity).lower_ref() == v
}
#[quickcheck]
fn functor_identity_option(x: Option<i32>) -> bool {
let coyo = ArcCoyoneda::<OptionBrand, _>::lift(x);
coyo.map(identity).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 = ArcCoyoneda::<VecBrand, _>::lift(v.clone()).map(compose(f, g)).lower_ref();
let right = ArcCoyoneda::<VecBrand, _>::lift(v).map(g).map(f).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 = ArcCoyoneda::<OptionBrand, _>::lift(x).map(compose(f, g)).lower_ref();
let right = ArcCoyoneda::<OptionBrand, _>::lift(x).map(g).map(f).lower_ref();
left == right
}
#[quickcheck]
fn collapse_preserves_value(v: Vec<i32>) -> bool {
let coyo = ArcCoyoneda::<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
}
#[quickcheck]
fn foldable_consistency_vec(v: Vec<i32>) -> bool {
let coyo = ArcCoyoneda::<VecBrand, _>::lift(v.clone()).map(|x: i32| x.wrapping_add(1));
let via_coyoneda: String =
explicit::fold_map::<RcFnBrand, ArcCoyonedaBrand<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
}
}
}