fp_library/classes/
foldable.rs

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