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