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::document_parameters;
21use fp_macros::document_signature;
22use fp_macros::document_type_parameters;
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	#[document_signature]
45	///
46	/// ### Type Parameters
47	///
48	#[document_type_parameters(
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	#[document_parameters(
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	#[document_signature]
106	///
107	/// ### Type Parameters
108	///
109	#[document_type_parameters(
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	#[document_parameters(
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	#[document_signature]
175	///
176	/// ### Type Parameters
177	///
178	#[document_type_parameters(
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	#[document_parameters(
189		"The function to map each element to a monoid.",
190		"The structure to fold."
191	)]
192	///
193	/// ### Returns
194	///
195	/// The combined monoid value.
196	///
197	/// ### Examples
198	///
199	/// ```
200	/// use fp_library::{brands::*, functions::*};
201	///
202	/// let x = Some(5);
203	/// let y = fold_map::<RcFnBrand, OptionBrand, _, _, _>(|a: i32| a.to_string(), x);
204	/// assert_eq!(y, "5".to_string());
205	/// ```
206	fn fold_map<'a, FnBrand, A: 'a + Clone, M, Func>(
207		func: Func,
208		fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
209	) -> M
210	where
211		M: Monoid + 'a,
212		Func: Fn(A) -> M + 'a,
213		FnBrand: CloneableFn + 'a,
214	{
215		Self::fold_right::<FnBrand, A, M, _>(move |a, m| M::append(func(a), m), M::empty(), fa)
216	}
217}
218
219/// Folds the structure by applying a function from right to left.
220///
221/// Free function version that dispatches to [the type class' associated function][`Foldable::fold_right`].
222///
223/// ### Type Signature
224///
225#[document_signature]
226///
227/// ### Type Parameters
228///
229#[document_type_parameters(
230	"The lifetime of the elements.",
231	"The brand of the cloneable function to use.",
232	"The brand of the foldable structure.",
233	"The type of the elements in the structure.",
234	"The type of the accumulator.",
235	"The type of the folding function."
236)]
237///
238/// ### Parameters
239///
240#[document_parameters(
241	"The function to apply to each element and the accumulator.",
242	"The initial value of the accumulator.",
243	"The structure to fold."
244)]
245///
246/// ### Returns
247///
248/// The final accumulator value.
249///
250/// ### Examples
251///
252/// ```
253/// use fp_library::{brands::*, functions::*};
254///
255/// let x = Some(5);
256/// let y = fold_right::<RcFnBrand, OptionBrand, _, _, _>(|a, b| a + b, 10, x);
257/// assert_eq!(y, 15);
258/// ```
259pub fn fold_right<'a, FnBrand, Brand: Foldable, A: 'a + Clone, B: 'a, Func>(
260	func: Func,
261	initial: B,
262	fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
263) -> B
264where
265	Func: Fn(A, B) -> B + 'a,
266	FnBrand: CloneableFn + 'a,
267{
268	Brand::fold_right::<FnBrand, A, B, Func>(func, initial, fa)
269}
270
271/// Folds the structure by applying a function from left to right.
272///
273/// Free function version that dispatches to [the type class' associated function][`Foldable::fold_left`].
274///
275/// ### Type Signature
276///
277#[document_signature]
278///
279/// ### Type Parameters
280///
281#[document_type_parameters(
282	"The lifetime of the elements.",
283	"The brand of the cloneable function to use.",
284	"The brand of the foldable structure.",
285	"The type of the elements in the structure.",
286	"The type of the accumulator.",
287	"The type of the folding function."
288)]
289///
290/// ### Parameters
291///
292#[document_parameters(
293	"The function to apply to the accumulator and each element.",
294	"The initial value of the accumulator.",
295	"The structure to fold."
296)]
297///
298/// ### Returns
299///
300/// The final accumulator value.
301///
302/// ### Examples
303///
304/// ```
305/// use fp_library::{brands::*, functions::*};
306///
307/// let x = Some(5);
308/// let y = fold_left::<RcFnBrand, OptionBrand, _, _, _>(|b, a| b + a, 10, x);
309/// assert_eq!(y, 15);
310/// ```
311pub fn fold_left<'a, FnBrand, Brand: Foldable, A: 'a + Clone, B: 'a, Func>(
312	func: Func,
313	initial: B,
314	fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
315) -> B
316where
317	Func: Fn(B, A) -> B + 'a,
318	FnBrand: CloneableFn + 'a,
319{
320	Brand::fold_left::<FnBrand, A, B, Func>(func, initial, fa)
321}
322
323/// Maps values to a monoid and combines them.
324///
325/// Free function version that dispatches to [the type class' associated function][`Foldable::fold_map`].
326///
327/// ### Type Signature
328///
329#[document_signature]
330///
331/// ### Type Parameters
332///
333#[document_type_parameters(
334	"The lifetime of the elements.",
335	"The brand of the cloneable function to use.",
336	"The brand of the foldable structure.",
337	"The type of the elements in the structure.",
338	"The type of the monoid.",
339	"The type of the mapping function."
340)]
341///
342/// ### Parameters
343///
344#[document_parameters("The function to map each element to a monoid.", "The structure to fold.")]
345///
346/// ### Returns
347///
348/// The combined monoid value.
349///
350/// ### Examples
351///
352/// ```
353/// use fp_library::{brands::*, functions::*};
354///
355/// let x = Some(5);
356/// let y = fold_map::<RcFnBrand, OptionBrand, _, _, _>(|a: i32| a.to_string(), x);
357/// assert_eq!(y, "5".to_string());
358/// ```
359pub fn fold_map<'a, FnBrand, Brand: Foldable, A: 'a + Clone, M, Func>(
360	func: Func,
361	fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
362) -> M
363where
364	M: Monoid + 'a,
365	Func: Fn(A) -> M + 'a,
366	FnBrand: CloneableFn + 'a,
367{
368	Brand::fold_map::<FnBrand, A, M, Func>(func, fa)
369}