Skip to main content

fp_library/classes/
foldable.rs

1//! Data structures that can be folded into a single value from the left or right.
2//!
3//! ### Examples
4//!
5//! ```
6//! use fp_library::{brands::*, functions::*};
7//!
8//! let x = Some(5);
9//! let y = fold_right::<RcFnBrand, OptionBrand, _, _, _>(|a, b| a + b, 10, x);
10//! assert_eq!(y, 15);
11//! ```
12
13use super::monoid::Monoid;
14use crate::{
15	Apply,
16	classes::{cloneable_fn::CloneableFn, semigroup::Semigroup},
17	kinds::*,
18	types::Endofunction,
19};
20use fp_macros::doc_params;
21use fp_macros::doc_type_params;
22use fp_macros::hm_signature;
23
24/// A type class for structures that can be folded to a single value.
25///
26/// A `Foldable` represents a structure that can be folded over to combine its elements
27/// into a single result.
28///
29/// ### Minimal Implementation
30///
31/// A minimal implementation of `Foldable` requires implementing either [`Foldable::fold_right`] or [`Foldable::fold_map`].
32///
33/// *   If [`Foldable::fold_right`] is implemented, [`Foldable::fold_map`] and [`Foldable::fold_left`] are derived from it.
34/// *   If [`Foldable::fold_map`] is implemented, [`Foldable::fold_right`] is derived from it, and [`Foldable::fold_left`] is derived from the derived [`Foldable::fold_right`].
35///
36/// Note that [`Foldable::fold_left`] is not sufficient on its own because the default implementations of [`Foldable::fold_right`] and [`Foldable::fold_map`] do not depend on it.
37pub trait Foldable: Kind_cdc7cd43dac7585f {
38	/// Folds the structure by applying a function from right to left.
39	///
40	/// This method performs a right-associative fold of the structure.
41	///
42	/// ### Type Signature
43	///
44	#[hm_signature(Foldable)]
45	///
46	/// ### Type Parameters
47	///
48	#[doc_type_params(
49		"The lifetime of the elements.",
50		"The brand of the cloneable function to use.",
51		"The type of the elements in the structure.",
52		"The type of the accumulator.",
53		"The type of the folding function."
54	)]
55	///
56	/// ### Parameters
57	///
58	#[doc_params(
59		"The function to apply to each element and the accumulator.",
60		"The initial value of the accumulator.",
61		"The structure to fold."
62	)]
63	///
64	/// ### Returns
65	///
66	/// The final accumulator value.
67	///
68	/// ### Examples
69	///
70	/// ```
71	/// use fp_library::{brands::*, functions::*};
72	///
73	/// let x = Some(5);
74	/// let y = fold_right::<RcFnBrand, OptionBrand, _, _, _>(|a, b| a + b, 10, x);
75	/// assert_eq!(y, 15);
76	/// ```
77	fn fold_right<'a, FnBrand, A: 'a + Clone, B: 'a, Func>(
78		func: Func,
79		initial: B,
80		fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
81	) -> B
82	where
83		Func: Fn(A, B) -> B + 'a,
84		FnBrand: CloneableFn + 'a,
85	{
86		let f = <FnBrand as CloneableFn>::new(move |(a, b)| func(a, b));
87		let m = Self::fold_map::<FnBrand, A, Endofunction<FnBrand, B>, _>(
88			move |a: A| {
89				let f = f.clone();
90				Endofunction::<FnBrand, B>::new(<FnBrand as CloneableFn>::new(move |b| {
91					f((a.clone(), b))
92				}))
93			},
94			fa,
95		);
96		m.0(initial)
97	}
98
99	/// Folds the structure by applying a function from left to right.
100	///
101	/// This method performs a left-associative fold of the structure.
102	///
103	/// ### Type Signature
104	///
105	#[hm_signature(Foldable)]
106	///
107	/// ### Type Parameters
108	///
109	#[doc_type_params(
110		"The lifetime of the elements.",
111		"The brand of the cloneable function to use.",
112		"The type of the elements in the structure.",
113		"The type of the accumulator.",
114		"The type of the folding function."
115	)]
116	///
117	/// ### Parameters
118	///
119	#[doc_params(
120		"The function to apply to the accumulator and each element.",
121		"The initial value of the accumulator.",
122		"The structure to fold."
123	)]
124	///
125	/// ### Returns
126	///
127	/// The final accumulator value.
128	///
129	/// ### Examples
130	///
131	/// ```
132	/// use fp_library::{brands::*, functions::*};
133	///
134	/// let x = Some(5);
135	/// let y = fold_left::<RcFnBrand, OptionBrand, _, _, _>(|b, a| b + a, 10, x);
136	/// assert_eq!(y, 15);
137	/// ```
138	fn fold_left<'a, FnBrand, A: 'a + Clone, B: 'a, Func>(
139		func: Func,
140		initial: B,
141		fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
142	) -> B
143	where
144		Func: Fn(B, A) -> B + 'a,
145		FnBrand: CloneableFn + 'a,
146	{
147		let f = <FnBrand as CloneableFn>::new(move |(b, a)| func(b, a));
148		let m = Self::fold_right::<FnBrand, A, Endofunction<FnBrand, B>, _>(
149			move |a: A, k: Endofunction<'a, FnBrand, B>| {
150				let f = f.clone();
151				// k is the "rest" of the computation.
152				// We want to perform "current" (f(b, a)) then "rest".
153				// Endofunction composition is f . g (f after g).
154				// So we want k . current.
155				// append(k, current).
156				let current =
157					Endofunction::<FnBrand, B>::new(<FnBrand as CloneableFn>::new(move |b| {
158						f((b, a.clone()))
159					}));
160				Semigroup::append(k, current)
161			},
162			Endofunction::<FnBrand, B>::empty(),
163			fa,
164		);
165		m.0(initial)
166	}
167
168	/// Maps values to a monoid and combines them.
169	///
170	/// This method maps each element of the structure to a monoid and then combines the results using the monoid's `append` operation.
171	///
172	/// ### Type Signature
173	///
174	#[hm_signature(Foldable)]
175	///
176	/// ### Type Parameters
177	///
178	#[doc_type_params(
179		"The lifetime of the elements.",
180		"The brand of the cloneable function to use.",
181		"The type of the elements in the structure.",
182		"The type of the monoid.",
183		"The type of the mapping function."
184	)]
185	///
186	/// ### Parameters
187	///
188	#[doc_params("The function to map each element to a monoid.", "The structure to fold.")]
189	///
190	/// ### Returns
191	///
192	/// The combined monoid value.
193	///
194	/// ### Examples
195	///
196	/// ```
197	/// use fp_library::{brands::*, functions::*};
198	///
199	/// let x = Some(5);
200	/// let y = fold_map::<RcFnBrand, OptionBrand, _, _, _>(|a: i32| a.to_string(), x);
201	/// assert_eq!(y, "5".to_string());
202	/// ```
203	fn fold_map<'a, FnBrand, A: 'a + Clone, M, Func>(
204		func: Func,
205		fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
206	) -> M
207	where
208		M: Monoid + 'a,
209		Func: Fn(A) -> M + 'a,
210		FnBrand: CloneableFn + 'a,
211	{
212		Self::fold_right::<FnBrand, A, M, _>(move |a, m| M::append(func(a), m), M::empty(), fa)
213	}
214}
215
216/// Folds the structure by applying a function from right to left.
217///
218/// Free function version that dispatches to [the type class' associated function][`Foldable::fold_right`].
219///
220/// ### Type Signature
221///
222#[hm_signature(Foldable)]
223///
224/// ### Type Parameters
225///
226#[doc_type_params(
227	"The lifetime of the elements.",
228	"The brand of the cloneable function to use.",
229	"The brand of the foldable structure.",
230	"The type of the elements in the structure.",
231	"The type of the accumulator.",
232	"The type of the folding function."
233)]
234///
235/// ### Parameters
236///
237#[doc_params(
238	"The function to apply to each element and the accumulator.",
239	"The initial value of the accumulator.",
240	"The structure to fold."
241)]
242///
243/// ### Returns
244///
245/// The final accumulator value.
246///
247/// ### Examples
248///
249/// ```
250/// use fp_library::{brands::*, functions::*};
251///
252/// let x = Some(5);
253/// let y = fold_right::<RcFnBrand, OptionBrand, _, _, _>(|a, b| a + b, 10, x);
254/// assert_eq!(y, 15);
255/// ```
256pub fn fold_right<'a, FnBrand, Brand: Foldable, A: 'a + Clone, B: 'a, Func>(
257	func: Func,
258	initial: B,
259	fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
260) -> B
261where
262	Func: Fn(A, B) -> B + 'a,
263	FnBrand: CloneableFn + 'a,
264{
265	Brand::fold_right::<FnBrand, A, B, Func>(func, initial, fa)
266}
267
268/// Folds the structure by applying a function from left to right.
269///
270/// Free function version that dispatches to [the type class' associated function][`Foldable::fold_left`].
271///
272/// ### Type Signature
273///
274#[hm_signature(Foldable)]
275///
276/// ### Type Parameters
277///
278#[doc_type_params(
279	"The lifetime of the elements.",
280	"The brand of the cloneable function to use.",
281	"The brand of the foldable structure.",
282	"The type of the elements in the structure.",
283	"The type of the accumulator.",
284	"The type of the folding function."
285)]
286///
287/// ### Parameters
288///
289#[doc_params(
290	"The function to apply to the accumulator and each element.",
291	"The initial value of the accumulator.",
292	"The structure to fold."
293)]
294///
295/// ### Returns
296///
297/// The final accumulator value.
298///
299/// ### Examples
300///
301/// ```
302/// use fp_library::{brands::*, functions::*};
303///
304/// let x = Some(5);
305/// let y = fold_left::<RcFnBrand, OptionBrand, _, _, _>(|b, a| b + a, 10, x);
306/// assert_eq!(y, 15);
307/// ```
308pub fn fold_left<'a, FnBrand, Brand: Foldable, A: 'a + Clone, B: 'a, Func>(
309	func: Func,
310	initial: B,
311	fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
312) -> B
313where
314	Func: Fn(B, A) -> B + 'a,
315	FnBrand: CloneableFn + 'a,
316{
317	Brand::fold_left::<FnBrand, A, B, Func>(func, initial, fa)
318}
319
320/// Maps values to a monoid and combines them.
321///
322/// Free function version that dispatches to [the type class' associated function][`Foldable::fold_map`].
323///
324/// ### Type Signature
325///
326#[hm_signature(Foldable)]
327///
328/// ### Type Parameters
329///
330#[doc_type_params(
331	"The lifetime of the elements.",
332	"The brand of the cloneable function to use.",
333	"The brand of the foldable structure.",
334	"The type of the elements in the structure.",
335	"The type of the monoid.",
336	"The type of the mapping function."
337)]
338///
339/// ### Parameters
340///
341#[doc_params("The function to map each element to a monoid.", "The structure to fold.")]
342///
343/// ### Returns
344///
345/// The combined monoid value.
346///
347/// ### Examples
348///
349/// ```
350/// use fp_library::{brands::*, functions::*};
351///
352/// let x = Some(5);
353/// let y = fold_map::<RcFnBrand, OptionBrand, _, _, _>(|a: i32| a.to_string(), x);
354/// assert_eq!(y, "5".to_string());
355/// ```
356pub fn fold_map<'a, FnBrand, Brand: Foldable, A: 'a + Clone, M, Func>(
357	func: Func,
358	fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
359) -> M
360where
361	M: Monoid + 'a,
362	Func: Fn(A) -> M + 'a,
363	FnBrand: CloneableFn + 'a,
364{
365	Brand::fold_map::<FnBrand, A, M, Func>(func, fa)
366}