fp_library/classes/foldable.rs
1use crate::{
2 classes::{ClonableFn, Monoid, Semigroup, clonable_fn::ApplyFn},
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: ApplyFn<'a, ClonableFnBrand, B, ApplyFn<'a, ClonableFnBrand, A, B>>
49 ) -> ApplyFn<'a, ClonableFnBrand, B, ApplyFn<'a, ClonableFnBrand, Apply0L1T<Self, A>, B>> {
50 ClonableFnBrand::new(move |b: B| {
51 ClonableFnBrand::new({
52 let f = f.clone();
53 move |fa| {
54 (((Self::fold_right::<ClonableFnBrand, _, _>(compose::<
55 ClonableFnBrand,
56 _,
57 _,
58 _,
59 >(flip::<
60 ClonableFnBrand,
61 _,
62 _,
63 _,
64 >(
65 ClonableFnBrand::new(compose::<ClonableFnBrand, _, _, _>),
66 ))(flip::<
67 ClonableFnBrand,
68 _,
69 _,
70 _,
71 >(f.clone()))))(ClonableFnBrand::new(identity)))(fa))(b.to_owned())
72 }
73 })
74 })
75 }
76
77 /// Maps values to a monoid and combines them.
78 ///
79 /// The default implementation of `fold_map` is implemented in terms of [`fold_right`], [`compose`], [`append`][crate::functions::append] and [`empty`][crate::functions::empty] where:
80 ///
81 /// `fold_map f = (fold_right ((compose append) f)) empty`
82 ///
83 /// # Type Signature
84 ///
85 /// `forall a. Foldable f, Monoid m => (a -> m) -> f a -> m`
86 ///
87 /// # Parameters
88 ///
89 /// * `f`: A function that converts from values into monoidal elements.
90 /// * `fa`: A foldable structure containing values of type `A`.
91 ///
92 /// # Returns
93 ///
94 /// Final monoid obtained from the folding operation.
95 ///
96 /// # Examples
97 ///
98 /// ```
99 /// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::{fold_map, identity}};
100 /// use std::rc::Rc;
101 ///
102 /// assert_eq!(
103 /// fold_map::<RcFnBrand, VecBrand, _, String>(Rc::new(identity))(vec![
104 /// "Hello, ".to_string(),
105 /// "World!".to_string()
106 /// ]),
107 /// "Hello, World!"
108 /// );
109 /// ```
110 fn fold_map<'a, ClonableFnBrand: 'a + ClonableFn, A: Clone, M: Monoid<'a> + Clone>(
111 f: ApplyFn<'a, ClonableFnBrand, A, M>
112 ) -> ApplyFn<'a, ClonableFnBrand, Apply0L1T<Self, A>, M> {
113 ClonableFnBrand::new(move |fa: Apply0L1T<Self, A>| {
114 ((Self::fold_right::<'a, ClonableFnBrand, A, M>((compose::<
115 'a,
116 ClonableFnBrand,
117 A,
118 M,
119 _,
120 >(ClonableFnBrand::new::<
121 'a,
122 M,
123 _,
124 >(
125 Semigroup::<'a>::append::<ClonableFnBrand>,
126 )))(f.clone())))(M::empty()))(fa)
127 })
128 }
129
130 /// Folds the structure by applying a function from right to left.
131 ///
132 /// The default implementation of `fold_right` is implemented in terms of [`fold_map`] using the [`Endofunction` monoid][`crate::types::Endofunction`] where:
133 ///
134 /// `((fold_right f) b) fa = ((fold_map f) fa) b`
135 ///
136 /// # Type Signature
137 ///
138 /// `forall a b. Foldable f => (a -> b -> b) -> b -> f a -> b`
139 ///
140 /// # Parameters
141 ///
142 /// * `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.
143 /// * `b`: Initial value of type `B`.
144 /// * `fa`: A foldable structure containing values of type `A`.
145 ///
146 /// # Returns
147 ///
148 /// Final value of type `B` obtained from the folding operation.
149 ///
150 /// # Examples
151 ///
152 /// ```
153 /// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::fold_right};
154 /// use std::rc::Rc;
155 ///
156 /// assert_eq!(
157 /// fold_right::<RcFnBrand, VecBrand, _, _>(Rc::new(|item| Rc::new(move |carry| carry * 2 + item)))(0)(vec![1, 2, 3]),
158 /// 17
159 /// );
160 /// ```
161 fn fold_right<'a, ClonableFnBrand: 'a + ClonableFn, A: Clone, B: Clone>(
162 f: ApplyFn<'a, ClonableFnBrand, A, ApplyFn<'a, ClonableFnBrand, B, B>>
163 ) -> ApplyFn<'a, ClonableFnBrand, B, ApplyFn<'a, ClonableFnBrand, Apply0L1T<Self, A>, B>> {
164 ClonableFnBrand::new(move |b: B| {
165 let f = f.clone();
166 ClonableFnBrand::new(move |fa: Apply0L1T<Self, A>| {
167 (Self::fold_map::<'a, ClonableFnBrand, _, _>(ClonableFnBrand::new({
168 let f = f.clone();
169 move |a: A| Endofunction::<'a, ClonableFnBrand, _>::new(f(a))
170 }))(fa)
171 .0)(b.clone())
172 })
173 })
174 }
175}
176
177/// Folds the structure by applying a function from left to right.
178///
179/// Free function version that dispatches to [the type class' associated function][`Foldable::fold_left`].
180///
181/// The default implementation of `fold_left` is implemented in terms of [`fold_right`], [`flip`], [`compose`] and [`identity`] where:
182///
183/// `((fold_left f) b) fa = (((fold_right (((compose (flip compose)) (flip f)))) identity) fa) b`
184///
185/// # Type Signature
186///
187/// `forall a b. Foldable f => (b -> a -> b) -> b -> f a -> b`
188///
189/// # Parameters
190///
191/// * `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.
192/// * `b`: Initial value of type `B`.
193/// * `fa`: A foldable structure containing values of type `A`.
194///
195/// # Returns
196///
197/// Final value of type `B` obtained from the folding operation.
198///
199/// # Examples
200///
201/// ```
202/// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::fold_left};
203/// use std::rc::Rc;
204///
205/// assert_eq!(
206/// fold_left::<RcFnBrand, VecBrand, _, _>(Rc::new(|carry| Rc::new(move |item| carry * 2 + item)))(0)(vec![1, 2, 3]),
207/// 11
208/// );
209/// ```
210pub fn fold_left<'a, ClonableFnBrand: 'a + ClonableFn, Brand: Foldable, A: Clone, B: Clone>(
211 f: ApplyFn<'a, ClonableFnBrand, B, ApplyFn<'a, ClonableFnBrand, A, B>>
212) -> ApplyFn<'a, ClonableFnBrand, B, ApplyFn<'a, ClonableFnBrand, Apply0L1T<Brand, A>, B>> {
213 Brand::fold_left::<ClonableFnBrand, _, _>(f)
214}
215
216/// Maps values to a monoid and combines them.
217///
218/// Free function version that dispatches to [the type class' associated function][`Foldable::fold_map`].
219///
220/// The default implementation of `fold_map` is implemented in terms of [`fold_right`], [`compose`], [`append`][crate::functions::append] and [`empty`][crate::functions::empty] where:
221///
222/// `fold_map f = (fold_right ((compose append) f)) empty`
223///
224/// # Type Signature
225///
226/// `forall a. Foldable f, Monoid m => (a -> m) -> f a -> m`
227///
228/// # Parameters
229///
230/// * `f`: A function that converts from values into monoidal elements.
231/// * `fa`: A foldable structure containing values of type `A`.
232///
233/// # Returns
234///
235/// Final monoid obtained from the folding operation.
236///
237/// # Examples
238///
239/// ```
240/// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::{fold_map, identity}};
241/// use std::rc::Rc;
242///
243/// assert_eq!(
244/// fold_map::<RcFnBrand, VecBrand, _, String>(Rc::new(identity))(vec![
245/// "Hello, ".to_string(),
246/// "World!".to_string()
247/// ]),
248/// "Hello, World!"
249/// );
250/// ```
251pub fn fold_map<
252 'a,
253 ClonableFnBrand: 'a + ClonableFn,
254 Brand: Foldable,
255 A: Clone,
256 M: Monoid<'a> + Clone,
257>(
258 f: ApplyFn<'a, ClonableFnBrand, A, M>
259) -> ApplyFn<'a, ClonableFnBrand, Apply0L1T<Brand, A>, M> {
260 Brand::fold_map::<ClonableFnBrand, _, M>(f)
261}
262
263/// Folds the structure by applying a function from right to left.
264///
265/// Free function version that dispatches to [the type class' associated function][`Foldable::fold_right`].
266///
267/// The default implementation of `fold_right` is implemented in terms of [`fold_map`] using the [`Endomorphism` monoid][`crate::types::Endomorphism`] where:
268///
269/// `((fold_right f) b) fa = ((fold_map f) fa) b`
270///
271/// # Type Signature
272///
273/// `forall a b. Foldable f => (a -> b -> b) -> b -> f a -> b`
274///
275/// # Parameters
276///
277/// * `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.
278/// * `b`: Initial value of type `B`.
279/// * `fa`: A foldable structure containing values of type `A`.
280///
281/// # Returns
282///
283/// Final value of type `B` obtained from the folding operation.
284///
285/// # Examples
286///
287/// ```
288/// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::fold_right};
289/// use std::rc::Rc;
290///
291/// assert_eq!(
292/// fold_right::<RcFnBrand, VecBrand, _, _>(Rc::new(|item| Rc::new(move |carry| carry * 2 + item)))(0)(vec![1, 2, 3]),
293/// 17
294/// );
295/// ```
296pub fn fold_right<'a, ClonableFnBrand: 'a + ClonableFn, Brand: Foldable, A: Clone, B: Clone>(
297 f: ApplyFn<'a, ClonableFnBrand, A, ApplyFn<'a, ClonableFnBrand, B, B>>
298) -> ApplyFn<'a, ClonableFnBrand, B, ApplyFn<'a, ClonableFnBrand, Apply0L1T<Brand, A>, B>> {
299 Brand::fold_right::<ClonableFnBrand, _, _>(f)
300}