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