fp_library/classes/foldable.rs
1use crate::{
2 classes::{ClonableFn, Monoid, Semigroup, clonable_fn::ApplyClonableFn},
3 functions::{compose, flip, identity},
4 hkt::{Apply0L1T, Kind0L1T},
5 types::Endofunction,
6};
7
8/// A type class for structures that can be folded to a single value.
9///
10/// A `Foldable` represents a structure that can be folded over to combine its elements
11/// into a single result. This is useful for operations like summing values, collecting into a collection,
12/// or applying monoidal operations.
13///
14/// A minimum implementation of `Foldable` requires the manual implementation of at least [`Foldable::fold_right`] or [`Foldable::fold_map`].
15pub trait Foldable: Kind0L1T {
16 /// Folds the structure by applying a function from left to right.
17 ///
18 /// The default implementation of `fold_left` is implemented in terms of [`fold_right`], [`flip`], [`compose`] and [`identity`] where:
19 ///
20 /// `((fold_left f) b) fa = (((fold_right (((compose (flip compose)) (flip f)))) identity) fa) b`
21 ///
22 /// # Type Signature
23 ///
24 /// `forall a b. Foldable f => (b -> a -> b) -> b -> f a -> b`
25 ///
26 /// # Parameters
27 ///
28 /// * `f`: A curried binary function that takes in the current value of the accumulator, the next item in the structure and returns the next value of accumulator.
29 /// * `b`: Initial value of type `B`.
30 /// * `fa`: A foldable structure containing values of type `A`.
31 ///
32 /// # Returns
33 ///
34 /// Final value of type `B` obtained from the folding operation.
35 ///
36 /// # Examples
37 ///
38 /// ```
39 /// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::fold_left};
40 /// use std::rc::Rc;
41 ///
42 /// assert_eq!(
43 /// fold_left::<RcFnBrand, VecBrand, _, _>(Rc::new(|carry| Rc::new(move |item| carry * 2 + item)))(0)(vec![1, 2, 3]),
44 /// 11
45 /// );
46 /// ```
47 fn fold_left<'a, ClonableFnBrand: 'a + ClonableFn, A: Clone, B: Clone>(
48 f: ApplyClonableFn<'a, ClonableFnBrand, B, ApplyClonableFn<'a, ClonableFnBrand, A, B>>
49 ) -> ApplyClonableFn<
50 'a,
51 ClonableFnBrand,
52 B,
53 ApplyClonableFn<'a, ClonableFnBrand, Apply0L1T<Self, A>, B>,
54 > {
55 <ClonableFnBrand as ClonableFn>::new(move |b: B| {
56 <ClonableFnBrand as ClonableFn>::new({
57 let f = f.clone();
58 move |fa| {
59 (((Self::fold_right::<ClonableFnBrand, _, _>(compose::<
60 ClonableFnBrand,
61 _,
62 _,
63 _,
64 >(flip::<
65 ClonableFnBrand,
66 _,
67 _,
68 _,
69 >(
70 <ClonableFnBrand as ClonableFn>::new(compose::<ClonableFnBrand, _, _, _>),
71 ))(flip::<
72 ClonableFnBrand,
73 _,
74 _,
75 _,
76 >(f.clone()))))(<ClonableFnBrand as ClonableFn>::new(identity)))(fa))(
77 b.to_owned()
78 )
79 }
80 })
81 })
82 }
83
84 /// Maps values to a monoid and combines them.
85 ///
86 /// The default implementation of `fold_map` is implemented in terms of [`fold_right`], [`compose`], [`append`][crate::functions::append] and [`empty`][crate::functions::empty] where:
87 ///
88 /// `fold_map f = (fold_right ((compose append) f)) empty`
89 ///
90 /// # Type Signature
91 ///
92 /// `forall a. Foldable f, Monoid m => (a -> m) -> f a -> m`
93 ///
94 /// # Parameters
95 ///
96 /// * `f`: A function that converts from values into monoidal elements.
97 /// * `fa`: A foldable structure containing values of type `A`.
98 ///
99 /// # Returns
100 ///
101 /// Final monoid obtained from the folding operation.
102 ///
103 /// # Examples
104 ///
105 /// ```
106 /// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::{fold_map, identity}};
107 /// use std::rc::Rc;
108 ///
109 /// assert_eq!(
110 /// fold_map::<RcFnBrand, VecBrand, _, String>(Rc::new(identity))(vec![
111 /// "Hello, ".to_string(),
112 /// "World!".to_string()
113 /// ]),
114 /// "Hello, World!"
115 /// );
116 /// ```
117 fn fold_map<'a, ClonableFnBrand: 'a + ClonableFn, A: Clone, M: Monoid<'a> + Clone>(
118 f: ApplyClonableFn<'a, ClonableFnBrand, A, M>
119 ) -> ApplyClonableFn<'a, ClonableFnBrand, Apply0L1T<Self, A>, M> {
120 <ClonableFnBrand as ClonableFn>::new(move |fa: Apply0L1T<Self, A>| {
121 ((Self::fold_right::<'a, ClonableFnBrand, A, M>((compose::<
122 'a,
123 ClonableFnBrand,
124 A,
125 M,
126 _,
127 >(
128 <ClonableFnBrand as ClonableFn>::new::<'a, M, _>(
129 Semigroup::<'a>::append::<ClonableFnBrand>,
130 ),
131 ))(f.clone())))(M::empty()))(fa)
132 })
133 }
134
135 /// Folds the structure by applying a function from right to left.
136 ///
137 /// The default implementation of `fold_right` is implemented in terms of [`fold_map`] using the [`Endofunction` monoid][`crate::types::Endofunction`] where:
138 ///
139 /// `((fold_right f) b) fa = ((fold_map f) fa) b`
140 ///
141 /// # Type Signature
142 ///
143 /// `forall a b. Foldable f => (a -> b -> b) -> b -> f a -> b`
144 ///
145 /// # Parameters
146 ///
147 /// * `f`: A curried binary function that takes in the next item in the structure, the current value of the accumulator and returns the next value of accumulator.
148 /// * `b`: Initial value of type `B`.
149 /// * `fa`: A foldable structure containing values of type `A`.
150 ///
151 /// # Returns
152 ///
153 /// Final value of type `B` obtained from the folding operation.
154 ///
155 /// # Examples
156 ///
157 /// ```
158 /// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::fold_right};
159 /// use std::rc::Rc;
160 ///
161 /// assert_eq!(
162 /// fold_right::<RcFnBrand, VecBrand, _, _>(Rc::new(|item| Rc::new(move |carry| carry * 2 + item)))(0)(vec![1, 2, 3]),
163 /// 17
164 /// );
165 /// ```
166 fn fold_right<'a, ClonableFnBrand: 'a + ClonableFn, A: Clone, B: Clone>(
167 f: ApplyClonableFn<'a, ClonableFnBrand, A, ApplyClonableFn<'a, ClonableFnBrand, B, B>>
168 ) -> ApplyClonableFn<
169 'a,
170 ClonableFnBrand,
171 B,
172 ApplyClonableFn<'a, ClonableFnBrand, Apply0L1T<Self, A>, B>,
173 > {
174 <ClonableFnBrand as ClonableFn>::new(move |b: B| {
175 let f = f.clone();
176 <ClonableFnBrand as ClonableFn>::new(move |fa: Apply0L1T<Self, A>| {
177 (Self::fold_map::<'a, ClonableFnBrand, _, _>(<ClonableFnBrand as ClonableFn>::new(
178 {
179 let f = f.clone();
180 move |a: A| Endofunction::<'a, ClonableFnBrand, _>::new(f(a))
181 },
182 ))(fa)
183 .0)(b.clone())
184 })
185 })
186 }
187}
188
189/// Folds the structure by applying a function from left to right.
190///
191/// Free function version that dispatches to [the type class' associated function][`Foldable::fold_left`].
192///
193/// The default implementation of `fold_left` is implemented in terms of [`fold_right`], [`flip`], [`compose`] and [`identity`] where:
194///
195/// `((fold_left f) b) fa = (((fold_right (((compose (flip compose)) (flip f)))) identity) fa) b`
196///
197/// # Type Signature
198///
199/// `forall a b. Foldable f => (b -> a -> b) -> b -> f a -> b`
200///
201/// # Parameters
202///
203/// * `f`: A curried binary function that takes in the current value of the accumulator, the next item in the structure and returns the next value of accumulator.
204/// * `b`: Initial value of type `B`.
205/// * `fa`: A foldable structure containing values of type `A`.
206///
207/// # Returns
208///
209/// Final value of type `B` obtained from the folding operation.
210///
211/// # Examples
212///
213/// ```
214/// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::fold_left};
215/// use std::rc::Rc;
216///
217/// assert_eq!(
218/// fold_left::<RcFnBrand, VecBrand, _, _>(Rc::new(|carry| Rc::new(move |item| carry * 2 + item)))(0)(vec![1, 2, 3]),
219/// 11
220/// );
221/// ```
222pub fn fold_left<'a, ClonableFnBrand: 'a + ClonableFn, Brand: Foldable, A: Clone, B: Clone>(
223 f: ApplyClonableFn<'a, ClonableFnBrand, B, ApplyClonableFn<'a, ClonableFnBrand, A, B>>
224) -> ApplyClonableFn<
225 'a,
226 ClonableFnBrand,
227 B,
228 ApplyClonableFn<'a, ClonableFnBrand, Apply0L1T<Brand, A>, B>,
229> {
230 Brand::fold_left::<ClonableFnBrand, _, _>(f)
231}
232
233/// Maps values to a monoid and combines them.
234///
235/// Free function version that dispatches to [the type class' associated function][`Foldable::fold_map`].
236///
237/// The default implementation of `fold_map` is implemented in terms of [`fold_right`], [`compose`], [`append`][crate::functions::append] and [`empty`][crate::functions::empty] where:
238///
239/// `fold_map f = (fold_right ((compose append) f)) empty`
240///
241/// # Type Signature
242///
243/// `forall a. Foldable f, Monoid m => (a -> m) -> f a -> m`
244///
245/// # Parameters
246///
247/// * `f`: A function that converts from values into monoidal elements.
248/// * `fa`: A foldable structure containing values of type `A`.
249///
250/// # Returns
251///
252/// Final monoid obtained from the folding operation.
253///
254/// # Examples
255///
256/// ```
257/// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::{fold_map, identity}};
258/// use std::rc::Rc;
259///
260/// assert_eq!(
261/// fold_map::<RcFnBrand, VecBrand, _, String>(Rc::new(identity))(vec![
262/// "Hello, ".to_string(),
263/// "World!".to_string()
264/// ]),
265/// "Hello, World!"
266/// );
267/// ```
268pub fn fold_map<
269 'a,
270 ClonableFnBrand: 'a + ClonableFn,
271 Brand: Foldable,
272 A: Clone,
273 M: Monoid<'a> + Clone,
274>(
275 f: ApplyClonableFn<'a, ClonableFnBrand, A, M>
276) -> ApplyClonableFn<'a, ClonableFnBrand, Apply0L1T<Brand, A>, M> {
277 Brand::fold_map::<ClonableFnBrand, _, M>(f)
278}
279
280/// Folds the structure by applying a function from right to left.
281///
282/// Free function version that dispatches to [the type class' associated function][`Foldable::fold_right`].
283///
284/// The default implementation of `fold_right` is implemented in terms of [`fold_map`] using the [`Endomorphism` monoid][`crate::types::Endomorphism`] where:
285///
286/// `((fold_right f) b) fa = ((fold_map f) fa) b`
287///
288/// # Type Signature
289///
290/// `forall a b. Foldable f => (a -> b -> b) -> b -> f a -> b`
291///
292/// # Parameters
293///
294/// * `f`: A curried binary function that takes in the next item in the structure, the current value of the accumulator and returns the next value of accumulator.
295/// * `b`: Initial value of type `B`.
296/// * `fa`: A foldable structure containing values of type `A`.
297///
298/// # Returns
299///
300/// Final value of type `B` obtained from the folding operation.
301///
302/// # Examples
303///
304/// ```
305/// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::fold_right};
306/// use std::rc::Rc;
307///
308/// assert_eq!(
309/// fold_right::<RcFnBrand, VecBrand, _, _>(Rc::new(|item| Rc::new(move |carry| carry * 2 + item)))(0)(vec![1, 2, 3]),
310/// 17
311/// );
312/// ```
313pub fn fold_right<'a, ClonableFnBrand: 'a + ClonableFn, Brand: Foldable, A: Clone, B: Clone>(
314 f: ApplyClonableFn<'a, ClonableFnBrand, A, ApplyClonableFn<'a, ClonableFnBrand, B, B>>
315) -> ApplyClonableFn<
316 'a,
317 ClonableFnBrand,
318 B,
319 ApplyClonableFn<'a, ClonableFnBrand, Apply0L1T<Brand, A>, B>,
320> {
321 Brand::fold_right::<ClonableFnBrand, _, _>(f)
322}