fp_library/classes/
foldable.rs

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