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