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