fp_library/classes/
foldable.rs

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