Skip to main content

fp_library/types/optics/
fold.rs

1//! Fold optics for collecting multiple values.
2//!
3//! A fold represents a way to focus on zero or more values in a structure.
4
5#[fp_macros::document_module]
6mod inner {
7	use {
8		crate::{
9			Apply,
10			brands::optics::*,
11			classes::{
12				UnsizedCoercible,
13				monoid::Monoid,
14				optics::*,
15			},
16			kinds::*,
17			types::optics::Forget,
18		},
19		fp_macros::*,
20		std::marker::PhantomData,
21	};
22
23	pub use crate::classes::optics::fold::FoldFunc;
24
25	/// A wrapper that converts a function returning an iterable into a [`FoldFunc`].
26	///
27	/// `IterableFoldFn(f)` where `f: S -> impl IntoIterator<Item = A>` implements
28	/// [`FoldFunc<S, A>`] by iterating over `f(s)` and folding with the monoid.
29	/// This avoids allocating an intermediate `Vec<A>` when the source is already
30	/// an iterable structure (e.g., a `Vec<A>`) or when a lazy iterator suffices.
31	#[document_type_parameters("The type of the inner function.")]
32	pub struct IterableFoldFn<F>(pub F);
33
34	#[document_type_parameters("The type of the inner function.")]
35	#[document_parameters("The fold instance.")]
36	impl<F: Clone> Clone for IterableFoldFn<F> {
37		#[document_signature]
38		#[document_returns("A new `Fold` instance that is a copy of the original.")]
39		#[document_examples]
40		///
41		/// ```
42		/// use fp_library::types::optics::IterableFoldFn;
43		///
44		/// let f = IterableFoldFn(|v: Vec<i32>| v);
45		/// let cloned = f.clone();
46		/// assert_eq!(cloned.0(vec![1, 2]), vec![1, 2]);
47		/// ```
48		fn clone(&self) -> Self {
49			IterableFoldFn(self.0.clone())
50		}
51	}
52
53	#[document_type_parameters(
54		"The lifetime of the function.",
55		"The source type of the structure.",
56		"The type of the focuses.",
57		"The iterable type returned by the function.",
58		"The type of the inner function."
59	)]
60	#[document_parameters("The fold instance.")]
61	impl<'a, S, A, I, F> FoldFunc<'a, S, A> for IterableFoldFn<F>
62	where
63		F: Fn(S) -> I,
64		I: IntoIterator<Item = A>,
65	{
66		#[document_signature]
67		#[document_type_parameters("The monoid type to fold into.")]
68		#[document_parameters("The mapping function.", "The structure to fold.")]
69		#[document_returns("The combined monoid value.")]
70		#[document_examples]
71		///
72		/// ```
73		/// use fp_library::{
74		/// 	brands::optics::*,
75		/// 	classes::monoid::Monoid,
76		/// 	types::optics::{
77		/// 		FoldFunc,
78		/// 		IterableFoldFn,
79		/// 	},
80		/// };
81		///
82		/// let fold = IterableFoldFn(|v: Vec<i32>| v);
83		/// let result = fold.apply::<String>(|x| x.to_string(), vec![1, 2, 3]);
84		/// assert_eq!(result, "123".to_string());
85		/// ```
86		fn apply<R: Monoid>(
87			&self,
88			f: impl Fn(A) -> R + 'a,
89			s: S,
90		) -> R {
91			(self.0)(s).into_iter().fold(R::empty(), |r, a| R::append(r, f(a)))
92		}
93	}
94
95	/// A polymorphic fold.
96	///
97	/// Matches PureScript's `Fold r s t a b`.
98	#[document_type_parameters(
99		"The lifetime of the values.",
100		"The reference-counted pointer type.",
101		"The source type of the structure.",
102		"The target type of the structure.",
103		"The source type of the focus.",
104		"The target type of the focus.",
105		"The type of the fold function."
106	)]
107	pub struct Fold<'a, PointerBrand, S, T, A, B, F>
108	where
109		PointerBrand: UnsizedCoercible,
110		F: FoldFunc<'a, S, A>,
111		S: 'a,
112		T: 'a,
113		A: 'a,
114		B: 'a, {
115		/// The fold function.
116		pub fold_fn: F,
117		pub(crate) _phantom: PhantomData<(&'a (S, T, A, B), PointerBrand)>,
118	}
119
120	#[document_type_parameters(
121		"The lifetime of the values.",
122		"The reference-counted pointer type.",
123		"The source type of the structure.",
124		"The target type of the structure.",
125		"The source type of the focus.",
126		"The target type of the focus.",
127		"The type of the fold function."
128	)]
129	#[document_parameters("The fold instance.")]
130	impl<'a, PointerBrand, S, T, A, B, F> Clone for Fold<'a, PointerBrand, S, T, A, B, F>
131	where
132		PointerBrand: UnsizedCoercible,
133		F: FoldFunc<'a, S, A> + Clone,
134		S: 'a,
135		T: 'a,
136		A: 'a,
137		B: 'a,
138	{
139		#[document_signature]
140		#[document_returns("A new `Fold` instance that is a copy of the original.")]
141		#[document_examples]
142		///
143		/// ```
144		/// use fp_library::{
145		/// 	brands::{
146		/// 		RcBrand,
147		/// 		optics::*,
148		/// 	},
149		/// 	types::optics::{
150		/// 		Fold,
151		/// 		IterableFoldFn,
152		/// 	},
153		/// };
154		///
155		/// let f: Fold<RcBrand, Vec<i32>, Vec<i32>, i32, i32, _> =
156		/// 	Fold::new(IterableFoldFn(|v: Vec<i32>| v));
157		/// let cloned = f.clone();
158		/// assert_eq!(cloned.to_vec(vec![1, 2]), vec![1, 2]);
159		/// ```
160		fn clone(&self) -> Self {
161			Fold {
162				fold_fn: self.fold_fn.clone(),
163				_phantom: PhantomData,
164			}
165		}
166	}
167
168	#[document_type_parameters(
169		"The lifetime of the values.",
170		"The reference-counted pointer type.",
171		"The source type of the structure.",
172		"The target type of the structure.",
173		"The source type of the focus.",
174		"The target type of the focus.",
175		"The type of the fold function."
176	)]
177	#[document_parameters("The fold instance.")]
178	impl<'a, PointerBrand, S, T, A, B, F> Fold<'a, PointerBrand, S, T, A, B, F>
179	where
180		PointerBrand: UnsizedCoercible,
181		F: FoldFunc<'a, S, A>,
182		S: 'a,
183		T: 'a,
184		A: 'a,
185		B: 'a,
186	{
187		/// Create a new polymorphic fold.
188		#[document_signature]
189		///
190		#[document_parameters("The fold function.")]
191		///
192		#[document_returns("A new instance of the type.")]
193		///
194		#[document_examples]
195		///
196		/// ```
197		/// use fp_library::{
198		/// 	brands::{
199		/// 		RcBrand,
200		/// 		optics::*,
201		/// 	},
202		/// 	types::optics::{
203		/// 		Fold,
204		/// 		IterableFoldFn,
205		/// 	},
206		/// };
207		///
208		/// let f: Fold<RcBrand, Vec<i32>, Vec<i32>, i32, i32, _> =
209		/// 	Fold::new(IterableFoldFn(|v: Vec<i32>| v));
210		/// assert_eq!(f.to_vec(vec![1, 2]), vec![1, 2]);
211		/// ```
212		pub fn new(fold_fn: F) -> Self {
213			Fold {
214				fold_fn,
215				_phantom: PhantomData,
216			}
217		}
218
219		/// Collect all the focuses of the fold into a `Vec`.
220		#[document_signature]
221		///
222		#[document_parameters("The structure to fold.")]
223		///
224		#[document_returns("A `Vec` containing all the focuses.")]
225		///
226		#[document_examples]
227		///
228		/// ```
229		/// use fp_library::{
230		/// 	brands::{
231		/// 		RcBrand,
232		/// 		optics::*,
233		/// 	},
234		/// 	types::optics::{
235		/// 		Fold,
236		/// 		IterableFoldFn,
237		/// 	},
238		/// };
239		///
240		/// let f: Fold<RcBrand, Vec<i32>, Vec<i32>, i32, i32, _> =
241		/// 	Fold::new(IterableFoldFn(|v: Vec<i32>| v));
242		/// assert_eq!(f.to_vec(vec![1, 2, 3]), vec![1, 2, 3]);
243		/// ```
244		pub fn to_vec(
245			&self,
246			s: S,
247		) -> Vec<A>
248		where
249			A: Clone, {
250			self.fold_fn.apply::<Vec<A>>(|a| vec![a], s)
251		}
252	}
253
254	#[document_type_parameters(
255		"The lifetime of the values.",
256		"The reference-counted pointer type.",
257		"The source type of the structure.",
258		"The target type of the structure.",
259		"The source type of the focus.",
260		"The target type of the focus.",
261		"The type of the fold function."
262	)]
263	#[document_parameters("The fold instance.")]
264	impl<'a, PointerBrand, S, T, A, B, F> FoldOptic<'a, S, A> for Fold<'a, PointerBrand, S, T, A, B, F>
265	where
266		PointerBrand: UnsizedCoercible,
267		F: FoldFunc<'a, S, A> + Clone + 'a,
268		S: 'a,
269		T: 'a,
270		A: 'a,
271		B: 'a,
272	{
273		#[document_signature]
274		#[document_type_parameters(
275			"The monoid type.",
276			"The reference-counted pointer type for the Forget brand."
277		)]
278		#[document_parameters("The profunctor value to transform.")]
279		#[document_returns("The transformed profunctor value.")]
280		#[document_examples]
281		///
282		/// ```
283		/// use fp_library::{
284		/// 	brands::{
285		/// 		optics::*,
286		/// 		*,
287		/// 	},
288		/// 	classes::optics::*,
289		/// 	functions::*,
290		/// 	types::optics::*,
291		/// };
292		///
293		/// let f_optic: Fold<RcBrand, Vec<i32>, Vec<i32>, i32, i32, _> =
294		/// 	Fold::new(IterableFoldFn(|v: Vec<i32>| v));
295		/// let f = Forget::<RcBrand, String, i32, i32>::new(|x: i32| x.to_string());
296		/// let folded: Forget<RcBrand, String, Vec<i32>, Vec<i32>> = FoldOptic::evaluate(&f_optic, f);
297		/// assert_eq!(folded.run(vec![1, 2, 3]), "123".to_string());
298		/// ```
299		fn evaluate<R: 'a + Monoid + 'static, Q: UnsizedCoercible + 'static>(
300			&self,
301			pab: Apply!(<ForgetBrand<Q, R> as Kind!( type Of<'b, X: 'b, Y: 'b>: 'b; )>::Of<'a, A, A>),
302		) -> Apply!(<ForgetBrand<Q, R> as Kind!( type Of<'b, X: 'b, Y: 'b>: 'b; )>::Of<'a, S, S>)
303		{
304			let fold_fn = self.fold_fn.clone();
305			Forget::<Q, R, S, S>::new(move |s: S| {
306				let pab_fn = pab.0.clone();
307				fold_fn.apply::<R>(move |a| (pab_fn)(a), s)
308			})
309		}
310	}
311
312	/// A concrete fold type where types do not change.
313	///
314	/// Matches PureScript's `Fold' r s a`.
315	#[document_type_parameters(
316		"The lifetime of the values.",
317		"The reference-counted pointer type.",
318		"The type of the structure.",
319		"The type of the focus.",
320		"The type of the fold function."
321	)]
322	pub struct FoldPrime<'a, PointerBrand, S, A, F>
323	where
324		PointerBrand: UnsizedCoercible,
325		F: FoldFunc<'a, S, A>,
326		S: 'a,
327		A: 'a, {
328		/// The fold function.
329		pub fold_fn: F,
330		pub(crate) _phantom: PhantomData<(&'a (S, A), PointerBrand)>,
331	}
332
333	#[document_type_parameters(
334		"The lifetime of the values.",
335		"The reference-counted pointer type.",
336		"The type of the structure.",
337		"The type of the focus.",
338		"The type of the fold function."
339	)]
340	#[document_parameters("The fold instance.")]
341	impl<'a, PointerBrand, S, A, F> Clone for FoldPrime<'a, PointerBrand, S, A, F>
342	where
343		PointerBrand: UnsizedCoercible,
344		F: FoldFunc<'a, S, A> + Clone,
345		S: 'a,
346		A: 'a,
347	{
348		#[document_signature]
349		#[document_returns("A new `FoldPrime` instance that is a copy of the original.")]
350		#[document_examples]
351		///
352		/// ```
353		/// use fp_library::{
354		/// 	brands::{
355		/// 		RcBrand,
356		/// 		optics::*,
357		/// 	},
358		/// 	types::optics::{
359		/// 		FoldPrime,
360		/// 		IterableFoldFn,
361		/// 	},
362		/// };
363		/// let f: FoldPrime<RcBrand, Vec<i32>, i32, _> = FoldPrime::new(IterableFoldFn(|v: Vec<i32>| v));
364		/// let cloned = f.clone();
365		/// assert_eq!(cloned.to_vec(vec![1, 2]), vec![1, 2]);
366		/// ```
367		fn clone(&self) -> Self {
368			FoldPrime {
369				fold_fn: self.fold_fn.clone(),
370				_phantom: PhantomData,
371			}
372		}
373	}
374
375	#[document_type_parameters(
376		"The lifetime of the values.",
377		"The reference-counted pointer type.",
378		"The type of the structure.",
379		"The type of the focus.",
380		"The type of the fold function."
381	)]
382	#[document_parameters("The fold instance.")]
383	impl<'a, PointerBrand, S, A, F> FoldPrime<'a, PointerBrand, S, A, F>
384	where
385		PointerBrand: UnsizedCoercible,
386		F: FoldFunc<'a, S, A>,
387		S: 'a,
388		A: 'a,
389	{
390		/// Create a new monomorphic fold.
391		#[document_signature]
392		///
393		#[document_parameters("The fold function.")]
394		///
395		#[document_returns("A new instance of the type.")]
396		///
397		#[document_examples]
398		///
399		/// ```
400		/// use fp_library::{
401		/// 	brands::{
402		/// 		RcBrand,
403		/// 		optics::*,
404		/// 	},
405		/// 	types::optics::{
406		/// 		FoldPrime,
407		/// 		IterableFoldFn,
408		/// 	},
409		/// };
410		///
411		/// let f: FoldPrime<RcBrand, Vec<i32>, i32, _> = FoldPrime::new(IterableFoldFn(|v: Vec<i32>| v));
412		/// assert_eq!(f.to_vec(vec![1, 2, 3]), vec![1, 2, 3]);
413		/// ```
414		pub fn new(fold_fn: F) -> Self {
415			FoldPrime {
416				fold_fn,
417				_phantom: PhantomData,
418			}
419		}
420
421		/// Collect all the focuses of the fold into a `Vec`.
422		#[document_signature]
423		///
424		#[document_parameters("The structure to fold.")]
425		///
426		#[document_returns("A `Vec` containing all the focuses.")]
427		///
428		#[document_examples]
429		///
430		/// ```
431		/// use fp_library::{
432		/// 	brands::{
433		/// 		RcBrand,
434		/// 		optics::*,
435		/// 	},
436		/// 	types::optics::{
437		/// 		FoldPrime,
438		/// 		IterableFoldFn,
439		/// 	},
440		/// };
441		///
442		/// let f: FoldPrime<RcBrand, Vec<i32>, i32, _> = FoldPrime::new(IterableFoldFn(|v: Vec<i32>| v));
443		/// assert_eq!(f.to_vec(vec![1, 2, 3]), vec![1, 2, 3]);
444		/// ```
445		pub fn to_vec(
446			&self,
447			s: S,
448		) -> Vec<A>
449		where
450			A: Clone, {
451			self.fold_fn.apply::<Vec<A>>(|a| vec![a], s)
452		}
453	}
454
455	#[document_type_parameters(
456		"The lifetime of the values.",
457		"The reference-counted pointer type.",
458		"The type of the structure.",
459		"The type of the focus.",
460		"The type of the fold function."
461	)]
462	#[document_parameters("The fold instance.")]
463	impl<'a, PointerBrand, S, A, F> FoldOptic<'a, S, A> for FoldPrime<'a, PointerBrand, S, A, F>
464	where
465		PointerBrand: UnsizedCoercible,
466		F: FoldFunc<'a, S, A> + Clone + 'a,
467		S: 'a,
468		A: 'a,
469	{
470		#[document_signature]
471		#[document_type_parameters(
472			"The monoid type.",
473			"The reference-counted pointer type for the Forget brand."
474		)]
475		#[document_parameters("The profunctor value to transform.")]
476		#[document_returns("The transformed profunctor value.")]
477		#[document_examples]
478		///
479		/// ```
480		/// use fp_library::{
481		/// 	brands::{
482		/// 		optics::*,
483		/// 		*,
484		/// 	},
485		/// 	classes::optics::*,
486		/// 	functions::*,
487		/// 	types::optics::*,
488		/// };
489		///
490		/// let f_optic: FoldPrime<RcBrand, Vec<i32>, i32, _> =
491		/// 	FoldPrime::new(IterableFoldFn(|v: Vec<i32>| v));
492		/// let f = Forget::<RcBrand, String, i32, i32>::new(|x: i32| x.to_string());
493		/// let folded: Forget<RcBrand, String, Vec<i32>, Vec<i32>> = FoldOptic::evaluate(&f_optic, f);
494		/// assert_eq!(folded.run(vec![1, 2, 3]), "123".to_string());
495		/// ```
496		fn evaluate<R: 'a + Monoid + 'static, Q: UnsizedCoercible + 'static>(
497			&self,
498			pab: Apply!(<ForgetBrand<Q, R> as Kind!( type Of<'b, X: 'b, Y: 'b>: 'b; )>::Of<'a, A, A>),
499		) -> Apply!(<ForgetBrand<Q, R> as Kind!( type Of<'b, X: 'b, Y: 'b>: 'b; )>::Of<'a, S, S>)
500		{
501			let fold_fn = self.fold_fn.clone();
502			Forget::<Q, R, S, S>::new(move |s: S| {
503				let pab_fn = pab.0.clone();
504				fold_fn.apply::<R>(move |a| (pab_fn)(a), s)
505			})
506		}
507	}
508}
509pub use inner::*;