Skip to main content

fp_library/types/
rc_coyoneda.rs

1//! Reference-counted free functor with `Clone` support.
2//!
3//! [`RcCoyoneda`] is a variant of [`Coyoneda`](crate::types::Coyoneda) that wraps
4//! its inner layers in [`Rc`](std::rc::Rc) instead of [`Box`], making it `Clone`.
5//! This enables sharing and reuse of deferred map chains without consuming the value. The corresponding brand is [`RcCoyonedaBrand`](crate::brands::RcCoyonedaBrand).
6//!
7//! ## Trade-offs vs `Coyoneda`
8//!
9//! - **Clone:** `RcCoyoneda` is `Clone` (cheap refcount bump). `Coyoneda` is not.
10//! - **Allocation:** Each [`map`](RcCoyoneda::map) allocates 2 `Rc` values (one for
11//!   the layer, one for the function). `Coyoneda` allocates 1 `Box`.
12//! - **`lower_ref`:** [`lower_ref`](RcCoyoneda::lower_ref) borrows `&self` and
13//!   clones `F::Of<'a, A>` at the base layer. [`lower`](crate::types::Coyoneda::lower)
14//!   consumes `self` without cloning.
15//! - **Thread safety:** `RcCoyoneda` is `!Send`. Use
16//!   [`ArcCoyoneda`](crate::types::ArcCoyoneda) for thread-safe contexts.
17//!
18//! ## Stack safety
19//!
20//! Each chained [`map`](RcCoyoneda::map) adds a layer of recursion to
21//! [`lower_ref`](RcCoyoneda::lower_ref). Deep chains (thousands of maps) can overflow the stack.
22//! Three mitigations are available:
23//!
24//! 1. **`stacker` feature (automatic).** Enable the `stacker` feature flag to use
25//!    adaptive stack growth in `lower_ref`. This is transparent and handles arbitrarily
26//!    deep chains with near-zero overhead when the stack is sufficient.
27//! 2. **[`collapse`](RcCoyoneda::collapse) (manual).** Call `collapse()` periodically
28//!    to flatten accumulated layers. Requires `F: Functor`.
29//! 3. **[`CoyonedaExplicit`](crate::types::CoyonedaExplicit) with `.boxed()`.** An
30//!    alternative that accumulates maps without adding recursion depth.
31//!
32//! ### Examples
33//!
34//! ```
35//! use fp_library::{
36//! 	brands::*,
37//! 	functions::*,
38//! 	types::*,
39//! };
40//!
41//! let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x + 1).map(|x| x * 2);
42//!
43//! // Clone is cheap (refcount bump).
44//! let coyo2 = coyo.clone();
45//!
46//! assert_eq!(coyo.lower_ref(), vec![4, 6, 8]);
47//! assert_eq!(coyo2.lower_ref(), vec![4, 6, 8]);
48//! ```
49
50#[fp_macros::document_module]
51mod inner {
52	use {
53		crate::{
54			brands::RcCoyonedaBrand,
55			classes::{
56				Lift,
57				NaturalTransformation,
58				*,
59			},
60			impl_kind,
61			kinds::*,
62		},
63		fp_macros::*,
64		std::rc::Rc,
65	};
66
67	// -- Inner trait (borrow-based lowering for Rc) --
68
69	/// Trait for lowering an `RcCoyoneda` value back to its underlying functor
70	/// via a shared reference.
71	///
72	/// Unlike the `CoyonedaInner` trait which consumes `Box<Self>`,
73	/// this trait borrows `&self`, enabling `Clone` on the outer `Rc` wrapper.
74	/// The base layer clones `F::Of<'a, A>` to produce an owned value from
75	/// the borrow.
76	#[document_type_parameters(
77		"The lifetime of the values.",
78		"The brand of the underlying type constructor.",
79		"The output type of the accumulated mapping function."
80	)]
81	#[document_parameters("The trait object reference.")]
82	trait RcCoyonedaLowerRef<'a, F, A: 'a>: 'a
83	where
84		F: Kind_cdc7cd43dac7585f + 'a, {
85		/// Lower to the concrete functor by applying accumulated functions via `F::map`.
86		#[document_signature]
87		///
88		#[document_returns("The underlying functor value with accumulated functions applied.")]
89		#[document_examples]
90		///
91		/// ```
92		/// use fp_library::{
93		/// 	brands::*,
94		/// 	types::*,
95		/// };
96		///
97		/// let coyo = RcCoyoneda::<OptionBrand, _>::lift(Some(42));
98		/// assert_eq!(coyo.lower_ref(), Some(42));
99		/// ```
100		fn lower_ref(&self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
101		where
102			F: Functor;
103	}
104
105	// -- Base layer: wraps F<A> directly (identity mapping, clones on lower) --
106
107	/// Base layer created by [`RcCoyoneda::lift`]. Wraps `F A` with no mapping.
108	/// Clones the underlying value on each call to `lower_ref`.
109	struct RcCoyonedaBase<'a, F, A: 'a>
110	where
111		F: Kind_cdc7cd43dac7585f + 'a, {
112		fa: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
113	}
114
115	#[document_type_parameters(
116		"The lifetime of the values.",
117		"The brand of the underlying type constructor.",
118		"The type of the value inside the functor."
119	)]
120	#[document_parameters("The base layer instance.")]
121	impl<'a, F, A: 'a> RcCoyonedaLowerRef<'a, F, A> for RcCoyonedaBase<'a, F, A>
122	where
123		F: Kind_cdc7cd43dac7585f + 'a,
124		Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone,
125	{
126		/// Returns the wrapped value by cloning.
127		#[document_signature]
128		///
129		#[document_returns("A clone of the underlying functor value.")]
130		#[document_examples]
131		///
132		/// ```
133		/// use fp_library::{
134		/// 	brands::*,
135		/// 	types::*,
136		/// };
137		///
138		/// let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
139		/// assert_eq!(coyo.lower_ref(), vec![1, 2, 3]);
140		/// ```
141		fn lower_ref(&self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
142		where
143			F: Functor, {
144			self.fa.clone()
145		}
146	}
147
148	// -- Map layer: wraps an inner RcCoyoneda and adds an Rc-wrapped function --
149
150	/// Map layer created by [`RcCoyoneda::map`]. Stores the inner value (Rc-wrapped)
151	/// and an Rc-wrapped function to apply at lower time.
152	struct RcCoyonedaMapLayer<'a, F, B: 'a, A: 'a>
153	where
154		F: Kind_cdc7cd43dac7585f + 'a, {
155		inner: Rc<dyn RcCoyonedaLowerRef<'a, F, B> + 'a>,
156		func: Rc<dyn Fn(B) -> A + 'a>,
157	}
158
159	#[document_type_parameters(
160		"The lifetime of the values.",
161		"The brand of the underlying type constructor.",
162		"The input type of this layer's mapping function.",
163		"The output type of this layer's mapping function."
164	)]
165	#[document_parameters("The map layer instance.")]
166	impl<'a, F, B: 'a, A: 'a> RcCoyonedaLowerRef<'a, F, A> for RcCoyonedaMapLayer<'a, F, B, A>
167	where
168		F: Kind_cdc7cd43dac7585f + 'a,
169	{
170		/// Lowers the inner value, then applies this layer's function via `F::map`.
171		#[document_signature]
172		///
173		#[document_returns("The underlying functor value with this layer's function applied.")]
174		#[document_examples]
175		///
176		/// ```
177		/// use fp_library::{
178		/// 	brands::*,
179		/// 	types::*,
180		/// };
181		///
182		/// let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x + 1);
183		/// assert_eq!(coyo.lower_ref(), vec![2, 3, 4]);
184		/// ```
185		fn lower_ref(&self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
186		where
187			F: Functor, {
188			#[cfg(feature = "stacker")]
189			{
190				stacker::maybe_grow(32 * 1024, 1024 * 1024, || {
191					let lowered = self.inner.lower_ref();
192					let func = self.func.clone();
193					F::map(move |b| (*func)(b), lowered)
194				})
195			}
196			#[cfg(not(feature = "stacker"))]
197			{
198				let lowered = self.inner.lower_ref();
199				let func = self.func.clone();
200				F::map(move |b| (*func)(b), lowered)
201			}
202		}
203	}
204
205	// -- New layer: wraps F<B> and an Rc-wrapped function (single allocation) --
206
207	/// New layer created by [`RcCoyoneda::new`]. Stores the functor value and an
208	/// Rc-wrapped function, implementing `RcCoyonedaLowerRef` directly in a single
209	/// Rc allocation.
210	struct RcCoyonedaNewLayer<'a, F, B: 'a, A: 'a>
211	where
212		F: Kind_cdc7cd43dac7585f + 'a, {
213		fb: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
214		func: Rc<dyn Fn(B) -> A + 'a>,
215	}
216
217	#[document_type_parameters(
218		"The lifetime of the values.",
219		"The brand of the underlying type constructor.",
220		"The input type of the stored function.",
221		"The output type of the stored function."
222	)]
223	#[document_parameters("The new layer instance.")]
224	impl<'a, F, B: 'a, A: 'a> RcCoyonedaLowerRef<'a, F, A> for RcCoyonedaNewLayer<'a, F, B, A>
225	where
226		F: Kind_cdc7cd43dac7585f + 'a,
227		Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>): Clone,
228	{
229		/// Applies the stored function to the stored functor value via `F::map`.
230		#[document_signature]
231		///
232		#[document_returns("The underlying functor value with the stored function applied.")]
233		#[document_examples]
234		///
235		/// ```
236		/// use fp_library::{
237		/// 	brands::*,
238		/// 	types::*,
239		/// };
240		///
241		/// let coyo = RcCoyoneda::<VecBrand, _>::new(|x: i32| x * 2, vec![1, 2, 3]);
242		/// assert_eq!(coyo.lower_ref(), vec![2, 4, 6]);
243		/// ```
244		fn lower_ref(&self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
245		where
246			F: Functor, {
247			let func = self.func.clone();
248			F::map(move |b| (*func)(b), self.fb.clone())
249		}
250	}
251
252	// -- Outer type --
253
254	/// Reference-counted free functor with `Clone` support.
255	///
256	/// `RcCoyoneda` wraps its inner layers in [`Rc`], making the entire structure
257	/// cheaply cloneable (refcount bump). This enables sharing and reuse of
258	/// deferred map chains without consuming the value.
259	///
260	/// See the [module documentation](crate::types::rc_coyoneda) for trade-offs
261	/// and examples.
262	#[document_type_parameters(
263		"The lifetime of the values.",
264		"The brand of the underlying type constructor.",
265		"The current output type."
266	)]
267	pub struct RcCoyoneda<'a, F, A: 'a>(Rc<dyn RcCoyonedaLowerRef<'a, F, A> + 'a>)
268	where
269		F: Kind_cdc7cd43dac7585f + 'a;
270
271	#[document_type_parameters(
272		"The lifetime of the values.",
273		"The brand of the underlying type constructor.",
274		"The current output type."
275	)]
276	#[document_parameters("The `RcCoyoneda` instance to clone.")]
277	impl<'a, F, A: 'a> Clone for RcCoyoneda<'a, F, A>
278	where
279		F: Kind_cdc7cd43dac7585f + 'a,
280	{
281		/// Clones the `RcCoyoneda` by bumping the reference count. O(1).
282		#[document_signature]
283		///
284		#[document_returns("A new `RcCoyoneda` sharing the same inner layers.")]
285		#[document_examples]
286		///
287		/// ```
288		/// use fp_library::{
289		/// 	brands::*,
290		/// 	types::*,
291		/// };
292		///
293		/// let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
294		/// let coyo2 = coyo.clone();
295		/// assert_eq!(coyo.lower_ref(), coyo2.lower_ref());
296		/// ```
297		fn clone(&self) -> Self {
298			RcCoyoneda(self.0.clone())
299		}
300	}
301
302	#[document_type_parameters(
303		"The lifetime of the values.",
304		"The brand of the underlying type constructor.",
305		"The current output type."
306	)]
307	#[document_parameters("The `RcCoyoneda` instance.")]
308	impl<'a, F, A: 'a> RcCoyoneda<'a, F, A>
309	where
310		F: Kind_cdc7cd43dac7585f + 'a,
311	{
312		/// Lift a value of `F A` into `RcCoyoneda F A`.
313		///
314		/// Requires `F::Of<'a, A>: Clone` because [`lower_ref`](RcCoyoneda::lower_ref)
315		/// borrows `&self` and must produce an owned value by cloning at the base layer.
316		#[document_signature]
317		///
318		#[document_parameters("The functor value to lift.")]
319		///
320		#[document_returns("An `RcCoyoneda` wrapping the value.")]
321		#[document_examples]
322		///
323		/// ```
324		/// use fp_library::{
325		/// 	brands::*,
326		/// 	types::*,
327		/// };
328		///
329		/// let coyo = RcCoyoneda::<OptionBrand, _>::lift(Some(42));
330		/// assert_eq!(coyo.lower_ref(), Some(42));
331		/// ```
332		pub fn lift(fa: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)) -> Self
333		where
334			Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone, {
335			RcCoyoneda(Rc::new(RcCoyonedaBase {
336				fa,
337			}))
338		}
339
340		/// Lower the `RcCoyoneda` back to the underlying functor `F`.
341		///
342		/// Applies accumulated mapping functions via `F::map`. Requires `F: Functor`.
343		/// Unlike [`Coyoneda::lower`](crate::types::Coyoneda::lower), this borrows
344		/// `&self` rather than consuming it, cloning the base value.
345		#[document_signature]
346		///
347		#[document_returns("The underlying functor value with all accumulated functions applied.")]
348		#[document_examples]
349		///
350		/// ```
351		/// use fp_library::{
352		/// 	brands::*,
353		/// 	types::*,
354		/// };
355		///
356		/// let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x + 1);
357		/// assert_eq!(coyo.lower_ref(), vec![2, 3, 4]);
358		/// // Can call again since it borrows.
359		/// assert_eq!(coyo.lower_ref(), vec![2, 3, 4]);
360		/// ```
361		pub fn lower_ref(&self) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
362		where
363			F: Functor, {
364			self.0.lower_ref()
365		}
366
367		/// Flatten accumulated map layers into a single base layer.
368		///
369		/// Resets the recursion depth by lowering and re-lifting. Useful for
370		/// preventing stack overflow in deep chains when the `stacker` feature
371		/// is not enabled.
372		///
373		/// Requires `F: Functor` (for lowering) and `F::Of<'a, A>: Clone`
374		/// (for re-lifting).
375		#[document_signature]
376		///
377		#[document_returns("A new `RcCoyoneda` with a single base layer.")]
378		#[document_examples]
379		///
380		/// ```
381		/// use fp_library::{
382		/// 	brands::*,
383		/// 	types::*,
384		/// };
385		///
386		/// let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x + 1).map(|x| x * 2);
387		/// let collapsed = coyo.collapse();
388		/// assert_eq!(collapsed.lower_ref(), vec![4, 6, 8]);
389		/// ```
390		pub fn collapse(&self) -> RcCoyoneda<'a, F, A>
391		where
392			F: Functor,
393			Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone, {
394			RcCoyoneda::lift(self.lower_ref())
395		}
396
397		/// Fold the `RcCoyoneda` by lowering and delegating to `F::fold_map`.
398		///
399		/// Non-consuming alternative to the `Foldable` trait method, which takes
400		/// the value by move. This borrows `&self` via `lower_ref`.
401		#[document_signature]
402		///
403		#[document_type_parameters(
404			"The brand of the cloneable function to use.",
405			"The type of the monoid."
406		)]
407		///
408		#[document_parameters("The function to map each element to a monoid.")]
409		///
410		#[document_returns("The combined monoid value.")]
411		#[document_examples]
412		///
413		/// ```
414		/// use fp_library::{
415		/// 	brands::*,
416		/// 	types::*,
417		/// };
418		///
419		/// let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x * 10);
420		/// let result = coyo.fold_map::<RcFnBrand, _>(|x: i32| x.to_string());
421		/// assert_eq!(result, "102030");
422		/// // Can still use coyo after folding.
423		/// assert_eq!(coyo.lower_ref(), vec![10, 20, 30]);
424		/// ```
425		pub fn fold_map<FnBrand: LiftFn + 'a, M>(
426			&self,
427			func: impl Fn(A) -> M + 'a,
428		) -> M
429		where
430			F: Functor + Foldable,
431			A: Clone,
432			M: Monoid + 'a, {
433			F::fold_map::<FnBrand, A, M>(func, self.lower_ref())
434		}
435
436		/// Map a function over the `RcCoyoneda` value.
437		///
438		/// Wraps the function in [`Rc`] so it does not need to implement `Clone`.
439		/// The function is applied when [`lower_ref`](RcCoyoneda::lower_ref) is called.
440		#[document_signature]
441		///
442		#[document_type_parameters("The new output type after applying the function.")]
443		///
444		#[document_parameters("The function to apply.")]
445		///
446		#[document_returns("A new `RcCoyoneda` with the function stored for deferred application.")]
447		#[document_examples]
448		///
449		/// ```
450		/// use fp_library::{
451		/// 	brands::*,
452		/// 	types::*,
453		/// };
454		///
455		/// let coyo = RcCoyoneda::<OptionBrand, _>::lift(Some(5)).map(|x| x * 2).map(|x| x + 1);
456		/// assert_eq!(coyo.lower_ref(), Some(11));
457		/// ```
458		pub fn map<B: 'a>(
459			self,
460			f: impl Fn(A) -> B + 'a,
461		) -> RcCoyoneda<'a, F, B> {
462			RcCoyoneda(Rc::new(RcCoyonedaMapLayer {
463				inner: self.0,
464				func: Rc::new(f),
465			}))
466		}
467
468		/// Create an `RcCoyoneda` from a function and a functor value.
469		///
470		/// This is more efficient than `lift(fb).map(f)` because it creates
471		/// a single layer instead of two.
472		#[document_signature]
473		///
474		#[document_type_parameters("The input type of the function.")]
475		///
476		#[document_parameters("The function to defer.", "The functor value.")]
477		///
478		#[document_returns("An `RcCoyoneda` wrapping the value with the deferred function.")]
479		#[document_examples]
480		///
481		/// ```
482		/// use fp_library::{
483		/// 	brands::*,
484		/// 	types::*,
485		/// };
486		///
487		/// let coyo = RcCoyoneda::<VecBrand, _>::new(|x: i32| x * 2, vec![1, 2, 3]);
488		/// assert_eq!(coyo.lower_ref(), vec![2, 4, 6]);
489		/// ```
490		pub fn new<B: 'a>(
491			f: impl Fn(B) -> A + 'a,
492			fb: Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
493		) -> Self
494		where
495			Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>): Clone, {
496			RcCoyoneda(Rc::new(RcCoyonedaNewLayer {
497				fb,
498				func: Rc::new(f),
499			}))
500		}
501
502		/// Apply a natural transformation to change the underlying functor.
503		///
504		/// Lowers to `F A` (applying all accumulated maps), transforms via the
505		/// natural transformation, then re-lifts into `RcCoyoneda G A`.
506		///
507		/// Requires `F: Functor` for lowering.
508		#[document_signature]
509		///
510		#[document_type_parameters("The target functor brand.")]
511		///
512		#[document_parameters("The natural transformation from `F` to `G`.")]
513		///
514		#[document_returns("A new `RcCoyoneda` over the target functor `G`.")]
515		#[document_examples]
516		///
517		/// ```
518		/// use fp_library::{
519		/// 	brands::*,
520		/// 	classes::*,
521		/// 	types::*,
522		/// };
523		///
524		/// struct VecToOption;
525		/// impl NaturalTransformation<VecBrand, OptionBrand> for VecToOption {
526		/// 	fn transform<'a, A: 'a>(
527		/// 		&self,
528		/// 		fa: Vec<A>,
529		/// 	) -> Option<A> {
530		/// 		fa.into_iter().next()
531		/// 	}
532		/// }
533		///
534		/// let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![10, 20, 30]);
535		/// let hoisted = coyo.hoist(VecToOption);
536		/// assert_eq!(hoisted.lower_ref(), Some(10));
537		/// ```
538		pub fn hoist<G: Kind_cdc7cd43dac7585f + 'a>(
539			self,
540			nat: impl NaturalTransformation<F, G>,
541		) -> RcCoyoneda<'a, G, A>
542		where
543			F: Functor,
544			Apply!(<G as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone, {
545			RcCoyoneda::lift(nat.transform(self.lower_ref()))
546		}
547
548		/// Wrap a value in the `RcCoyoneda` context by delegating to `F::pure`.
549		///
550		/// Requires `F::Of<'a, A>: Clone` for the base layer.
551		#[document_signature]
552		///
553		#[document_parameters("The value to wrap.")]
554		///
555		#[document_returns("An `RcCoyoneda` containing the pure value.")]
556		#[document_examples]
557		///
558		/// ```
559		/// use fp_library::{
560		/// 	brands::*,
561		/// 	types::*,
562		/// };
563		///
564		/// let coyo = RcCoyoneda::<OptionBrand, i32>::pure(42);
565		/// assert_eq!(coyo.lower_ref(), Some(42));
566		/// ```
567		pub fn pure(a: A) -> Self
568		where
569			F: Pointed,
570			Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone, {
571			RcCoyoneda::lift(F::pure(a))
572		}
573
574		/// Chain `RcCoyoneda` computations by lowering, binding, and re-lifting.
575		///
576		/// This is a fusion barrier: all accumulated maps are applied before binding.
577		///
578		/// Requires `F: Functor` (for lowering) and `F: Semimonad` (for binding).
579		#[document_signature]
580		///
581		#[document_type_parameters("The output type of the bound computation.")]
582		///
583		#[document_parameters(
584			"The function to apply to the inner value, returning a new `RcCoyoneda`."
585		)]
586		///
587		#[document_returns("A new `RcCoyoneda` containing the bound result.")]
588		#[document_examples]
589		///
590		/// ```
591		/// use fp_library::{
592		/// 	brands::*,
593		/// 	types::*,
594		/// };
595		///
596		/// let coyo = RcCoyoneda::<OptionBrand, _>::lift(Some(5));
597		/// let result = coyo.bind(|x| RcCoyoneda::<OptionBrand, _>::lift(Some(x * 2)));
598		/// assert_eq!(result.lower_ref(), Some(10));
599		/// ```
600		pub fn bind<B: 'a>(
601			self,
602			func: impl Fn(A) -> RcCoyoneda<'a, F, B> + 'a,
603		) -> RcCoyoneda<'a, F, B>
604		where
605			F: Functor + Semimonad,
606			Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>): Clone, {
607			RcCoyoneda::lift(F::bind(self.lower_ref(), move |a| func(a).lower_ref()))
608		}
609
610		/// Apply a function inside an `RcCoyoneda` to a value inside another.
611		///
612		/// Both arguments are lowered and delegated to `F::apply`, then re-lifted.
613		/// This is a fusion barrier.
614		#[document_signature]
615		///
616		#[document_type_parameters(
617			"The brand of the cloneable function wrapper.",
618			"The type of the function input.",
619			"The type of the function output."
620		)]
621		///
622		#[document_parameters(
623			"The `RcCoyoneda` containing the function(s).",
624			"The `RcCoyoneda` containing the value(s)."
625		)]
626		///
627		#[document_returns("A new `RcCoyoneda` containing the applied result(s).")]
628		#[document_examples]
629		///
630		/// ```
631		/// use fp_library::{
632		/// 	brands::*,
633		/// 	classes::*,
634		/// 	functions::*,
635		/// 	types::*,
636		/// };
637		///
638		/// let ff =
639		/// 	RcCoyoneda::<OptionBrand, _>::lift(Some(lift_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2)));
640		/// let fa = RcCoyoneda::<OptionBrand, _>::lift(Some(5));
641		/// let result = RcCoyoneda::<OptionBrand, i32>::apply::<RcFnBrand, _, _>(ff, fa);
642		/// assert_eq!(result.lower_ref(), Some(10));
643		/// ```
644		pub fn apply<FnBrand: LiftFn + 'a, B: Clone + 'a, C: 'a>(
645			ff: RcCoyoneda<'a, F, <FnBrand as CloneFn>::Of<'a, B, C>>,
646			fa: RcCoyoneda<'a, F, B>,
647		) -> RcCoyoneda<'a, F, C>
648		where
649			F: Functor + Semiapplicative,
650			Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>): Clone, {
651			RcCoyoneda::lift(F::apply::<FnBrand, B, C>(ff.lower_ref(), fa.lower_ref()))
652		}
653
654		/// Lift a binary function into the `RcCoyoneda` context.
655		///
656		/// Both arguments are lowered and delegated to `F::lift2`, then re-lifted.
657		/// This is a fusion barrier.
658		#[document_signature]
659		///
660		#[document_type_parameters("The type of the second value.", "The type of the result.")]
661		///
662		#[document_parameters("The binary function to apply.", "The second `RcCoyoneda` value.")]
663		///
664		#[document_returns("An `RcCoyoneda` containing the result.")]
665		#[document_examples]
666		///
667		/// ```
668		/// use fp_library::{
669		/// 	brands::*,
670		/// 	types::*,
671		/// };
672		///
673		/// let a = RcCoyoneda::<OptionBrand, _>::lift(Some(3));
674		/// let b = RcCoyoneda::<OptionBrand, _>::lift(Some(4));
675		/// let result = a.lift2(|x, y| x + y, b);
676		/// assert_eq!(result.lower_ref(), Some(7));
677		/// ```
678		pub fn lift2<B: Clone + 'a, C: 'a>(
679			self,
680			func: impl Fn(A, B) -> C + 'a,
681			fb: RcCoyoneda<'a, F, B>,
682		) -> RcCoyoneda<'a, F, C>
683		where
684			F: Functor + Lift,
685			A: Clone,
686			Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>): Clone, {
687			RcCoyoneda::lift(F::lift2(func, self.lower_ref(), fb.lower_ref()))
688		}
689	}
690
691	// -- Brand --
692
693	impl_kind! {
694		impl<F: Kind_cdc7cd43dac7585f + 'static> for RcCoyonedaBrand<F> {
695			type Of<'a, A: 'a>: 'a = RcCoyoneda<'a, F, A>;
696		}
697	}
698
699	// -- Brand-level type class instances --
700	//
701	// RcCoyonedaBrand implements Functor and Foldable but NOT Pointed, Lift,
702	// Semiapplicative, or Semimonad. The blocker is a Clone bound that cannot
703	// be expressed in the trait method signatures:
704	//
705	// - RcCoyoneda wraps Rc<dyn RcCoyonedaLowerRef>. Constructing this requires
706	//   F::Of<'a, A>: Clone because RcCoyonedaBase's RcCoyonedaLowerRef impl has
707	//   that bound, needed to coerce the struct to the trait object.
708	// - CoyonedaBrand avoids this because Coyoneda::lift has no Clone requirement;
709	//   CoyonedaBase::lower consumes self: Box<Self> (moving, not cloning).
710	// - Rust does not allow adding extra where clauses to trait method impls beyond
711	//   what the trait definition specifies, so the Clone bound cannot be expressed.
712	//
713	// Inherent methods (pure, apply, bind, lift2) are provided instead, with the
714	// Clone bound specified directly on each method.
715
716	// -- Functor implementation --
717
718	#[document_type_parameters("The brand of the underlying type constructor.")]
719	impl<F: Kind_cdc7cd43dac7585f + 'static> Functor for RcCoyonedaBrand<F> {
720		/// Maps a function over the `RcCoyoneda` value by adding a new mapping layer.
721		///
722		/// Does not require `F: Functor`. The function is stored and applied at
723		/// [`lower_ref`](RcCoyoneda::lower_ref) time.
724		#[document_signature]
725		///
726		#[document_type_parameters(
727			"The lifetime of the values.",
728			"The type of the current output.",
729			"The type of the new output."
730		)]
731		///
732		#[document_parameters("The function to apply.", "The `RcCoyoneda` value.")]
733		///
734		#[document_returns("A new `RcCoyoneda` with the function stored for deferred application.")]
735		#[document_examples]
736		///
737		/// ```
738		/// use fp_library::{
739		/// 	brands::*,
740		/// 	functions::*,
741		/// 	types::*,
742		/// };
743		///
744		/// let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
745		/// let mapped = explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(|x| x * 10, coyo);
746		/// assert_eq!(mapped.lower_ref(), vec![10, 20, 30]);
747		/// ```
748		fn map<'a, A: 'a, B: 'a>(
749			func: impl Fn(A) -> B + 'a,
750			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
751		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
752			fa.map(func)
753		}
754	}
755
756	// -- Foldable implementation --
757
758	#[document_type_parameters("The brand of the underlying foldable functor.")]
759	impl<F: Functor + Foldable + 'static> Foldable for RcCoyonedaBrand<F> {
760		/// Folds the `RcCoyoneda` by lowering to the underlying functor and delegating.
761		///
762		/// Requires `F: Functor` (for lowering) and `F: Foldable`.
763		#[document_signature]
764		///
765		#[document_type_parameters(
766			"The lifetime of the elements.",
767			"The brand of the cloneable function to use.",
768			"The type of the elements in the structure.",
769			"The type of the monoid."
770		)]
771		///
772		#[document_parameters(
773			"The function to map each element to a monoid.",
774			"The `RcCoyoneda` structure to fold."
775		)]
776		///
777		#[document_returns("The combined monoid value.")]
778		#[document_examples]
779		///
780		/// ```
781		/// use fp_library::{
782		/// 	brands::*,
783		/// 	functions::*,
784		/// 	types::*,
785		/// };
786		///
787		/// let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x * 10);
788		/// let result = explicit::fold_map::<RcFnBrand, RcCoyonedaBrand<VecBrand>, _, _, _, _>(
789		/// 	|x: i32| x.to_string(),
790		/// 	coyo,
791		/// );
792		/// assert_eq!(result, "102030".to_string());
793		/// ```
794		fn fold_map<'a, FnBrand, A: 'a + Clone, M>(
795			func: impl Fn(A) -> M + 'a,
796			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
797		) -> M
798		where
799			M: Monoid + 'a,
800			FnBrand: LiftFn + 'a, {
801			F::fold_map::<FnBrand, A, M>(func, fa.lower_ref())
802		}
803	}
804
805	// -- Debug --
806
807	#[document_type_parameters(
808		"The lifetime of the values.",
809		"The brand of the underlying type constructor.",
810		"The current output type."
811	)]
812	#[document_parameters("The `RcCoyoneda` instance.")]
813	impl<'a, F, A: 'a> core::fmt::Debug for RcCoyoneda<'a, F, A>
814	where
815		F: Kind_cdc7cd43dac7585f + 'a,
816	{
817		/// Formats the `RcCoyoneda` as an opaque value.
818		///
819		/// The inner layers and functions cannot be inspected, so the output
820		/// is always `RcCoyoneda(<opaque>)`.
821		#[document_signature]
822		///
823		#[document_parameters("The formatter.")]
824		///
825		#[document_returns("The formatting result.")]
826		#[document_examples]
827		///
828		/// ```
829		/// use fp_library::{
830		/// 	brands::*,
831		/// 	types::*,
832		/// };
833		///
834		/// let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
835		/// assert_eq!(format!("{:?}", coyo), "RcCoyoneda(<opaque>)");
836		/// ```
837		fn fmt(
838			&self,
839			f: &mut core::fmt::Formatter<'_>,
840		) -> core::fmt::Result {
841			f.write_str("RcCoyoneda(<opaque>)")
842		}
843	}
844
845	// -- From<RcCoyoneda> for Coyoneda --
846
847	#[document_type_parameters(
848		"The lifetime of the values.",
849		"The brand of the underlying functor.",
850		"The type of the values."
851	)]
852	impl<'a, F, A: 'a> From<RcCoyoneda<'a, F, A>> for crate::types::Coyoneda<'a, F, A>
853	where
854		F: Kind_cdc7cd43dac7585f + Functor + 'a,
855	{
856		/// Convert an [`RcCoyoneda`] into a [`Coyoneda`](crate::types::Coyoneda)
857		/// by lowering to the underlying functor and re-lifting.
858		///
859		/// This applies all accumulated maps via `F::map` and clones the base value.
860		#[document_signature]
861		///
862		#[document_parameters("The `RcCoyoneda` to convert.")]
863		///
864		#[document_returns("A `Coyoneda` containing the lowered value.")]
865		#[document_examples]
866		///
867		/// ```
868		/// use fp_library::{
869		/// 	brands::*,
870		/// 	types::*,
871		/// };
872		///
873		/// let rc_coyo = RcCoyoneda::<OptionBrand, _>::lift(Some(5)).map(|x| x + 1);
874		/// let coyo: Coyoneda<OptionBrand, i32> = rc_coyo.into();
875		/// assert_eq!(coyo.lower(), Some(6));
876		/// ```
877		fn from(rc: RcCoyoneda<'a, F, A>) -> Self {
878			crate::types::Coyoneda::lift(rc.lower_ref())
879		}
880	}
881}
882
883pub use inner::*;
884
885#[cfg(test)]
886mod tests {
887	use crate::{
888		brands::*,
889		functions::*,
890		types::*,
891	};
892
893	#[test]
894	fn lift_lower_ref_identity_option() {
895		let coyo = RcCoyoneda::<OptionBrand, _>::lift(Some(42));
896		assert_eq!(coyo.lower_ref(), Some(42));
897	}
898
899	#[test]
900	fn lift_lower_ref_identity_vec() {
901		let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
902		assert_eq!(coyo.lower_ref(), vec![1, 2, 3]);
903	}
904
905	#[test]
906	fn clone_and_lower_ref() {
907		let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x + 1);
908		let coyo2 = coyo.clone();
909		assert_eq!(coyo.lower_ref(), vec![2, 3, 4]);
910		assert_eq!(coyo2.lower_ref(), vec![2, 3, 4]);
911	}
912
913	#[test]
914	fn chained_maps() {
915		let result = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3])
916			.map(|x| x + 1)
917			.map(|x| x * 2)
918			.map(|x| x.to_string())
919			.lower_ref();
920		assert_eq!(result, vec!["4", "6", "8"]);
921	}
922
923	#[test]
924	fn functor_identity_law() {
925		let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
926		let result =
927			explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(identity, coyo).lower_ref();
928		assert_eq!(result, vec![1, 2, 3]);
929	}
930
931	#[test]
932	fn functor_composition_law() {
933		let f = |x: i32| x + 1;
934		let g = |x: i32| x * 2;
935
936		let coyo1 = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
937		let left = explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(compose(f, g), coyo1)
938			.lower_ref();
939
940		let coyo2 = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]);
941		let right = explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(
942			f,
943			explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(g, coyo2),
944		)
945		.lower_ref();
946
947		assert_eq!(left, right);
948	}
949
950	// -- Foldable tests --
951
952	#[test]
953	fn fold_map_on_mapped() {
954		let coyo = RcCoyoneda::<VecBrand, _>::lift(vec![1, 2, 3]).map(|x| x * 10);
955		let result = explicit::fold_map::<RcFnBrand, RcCoyonedaBrand<VecBrand>, _, _, _, _>(
956			|x: i32| x.to_string(),
957			coyo,
958		);
959		assert_eq!(result, "102030".to_string());
960	}
961
962	#[test]
963	fn lower_ref_multiple_times() {
964		let coyo = RcCoyoneda::<OptionBrand, _>::lift(Some(5)).map(|x| x + 1);
965		assert_eq!(coyo.lower_ref(), Some(6));
966		assert_eq!(coyo.lower_ref(), Some(6));
967		assert_eq!(coyo.lower_ref(), Some(6));
968	}
969
970	#[test]
971	fn map_on_none_stays_none() {
972		let result = RcCoyoneda::<OptionBrand, _>::lift(None::<i32>)
973			.map(|x| x + 1)
974			.map(|x| x * 2)
975			.lower_ref();
976		assert_eq!(result, None);
977	}
978
979	// -- Property-based tests --
980
981	mod property {
982		use {
983			crate::{
984				brands::*,
985				functions::*,
986				types::*,
987			},
988			quickcheck_macros::quickcheck,
989		};
990
991		#[quickcheck]
992		fn functor_identity_vec(v: Vec<i32>) -> bool {
993			let coyo = RcCoyoneda::<VecBrand, _>::lift(v.clone());
994			explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(identity, coyo).lower_ref() == v
995		}
996
997		#[quickcheck]
998		fn functor_identity_option(x: Option<i32>) -> bool {
999			let coyo = RcCoyoneda::<OptionBrand, _>::lift(x);
1000			explicit::map::<RcCoyonedaBrand<OptionBrand>, _, _, _, _>(identity, coyo).lower_ref()
1001				== x
1002		}
1003
1004		#[quickcheck]
1005		fn functor_composition_vec(v: Vec<i32>) -> bool {
1006			let f = |x: i32| x.wrapping_add(1);
1007			let g = |x: i32| x.wrapping_mul(2);
1008
1009			let left = explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(
1010				compose(f, g),
1011				RcCoyoneda::<VecBrand, _>::lift(v.clone()),
1012			)
1013			.lower_ref();
1014
1015			let right = explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(
1016				f,
1017				explicit::map::<RcCoyonedaBrand<VecBrand>, _, _, _, _>(
1018					g,
1019					RcCoyoneda::<VecBrand, _>::lift(v),
1020				),
1021			)
1022			.lower_ref();
1023
1024			left == right
1025		}
1026
1027		#[quickcheck]
1028		fn functor_composition_option(x: Option<i32>) -> bool {
1029			let f = |x: i32| x.wrapping_add(1);
1030			let g = |x: i32| x.wrapping_mul(2);
1031
1032			let left = explicit::map::<RcCoyonedaBrand<OptionBrand>, _, _, _, _>(
1033				compose(f, g),
1034				RcCoyoneda::<OptionBrand, _>::lift(x),
1035			)
1036			.lower_ref();
1037
1038			let right = explicit::map::<RcCoyonedaBrand<OptionBrand>, _, _, _, _>(
1039				f,
1040				explicit::map::<RcCoyonedaBrand<OptionBrand>, _, _, _, _>(
1041					g,
1042					RcCoyoneda::<OptionBrand, _>::lift(x),
1043				),
1044			)
1045			.lower_ref();
1046
1047			left == right
1048		}
1049
1050		#[quickcheck]
1051		fn foldable_consistency_vec(v: Vec<i32>) -> bool {
1052			let coyo = RcCoyoneda::<VecBrand, _>::lift(v.clone()).map(|x: i32| x.wrapping_add(1));
1053			let via_coyoneda: String =
1054				explicit::fold_map::<RcFnBrand, RcCoyonedaBrand<VecBrand>, _, _, _, _>(
1055					|x: i32| x.to_string(),
1056					coyo,
1057				);
1058			let direct: String = explicit::fold_map::<RcFnBrand, VecBrand, _, _, _, _>(
1059				|x: i32| x.to_string(),
1060				v.iter().map(|x| x.wrapping_add(1)).collect::<Vec<_>>(),
1061			);
1062			via_coyoneda == direct
1063		}
1064
1065		#[quickcheck]
1066		fn collapse_preserves_value(v: Vec<i32>) -> bool {
1067			let coyo = RcCoyoneda::<VecBrand, _>::lift(v)
1068				.map(|x: i32| x.wrapping_add(1))
1069				.map(|x: i32| x.wrapping_mul(2));
1070			let before = coyo.lower_ref();
1071			let after = coyo.collapse().lower_ref();
1072			before == after
1073		}
1074	}
1075}