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