fp_library/typeclasses/foldable.rs
1use crate::{
2 functions::{compose, flip, identity},
3 hkt::{Apply0L1T, Kind0L1T},
4 typeclasses::{ClonableFn, Monoid, clonable_fn::ApplyFn},
5 types::Endomorphism,
6};
7
8/// A typeclass 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 f 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: 'a + Clone, B: 'a + 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 f a m. 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: 'a + Clone, M: 'a + Monoid + Clone>(
111 f: ApplyFn<'a, ClonableFnBrand, A, M>
112 ) -> ApplyFn<'a, ClonableFnBrand, Apply0L1T<Self, A>, M> {
113 ClonableFnBrand::new(move |fa| {
114 let f = f.clone();
115 ((Self::fold_right::<ClonableFnBrand, _, _>(ClonableFnBrand::new(move |a: A| {
116 let f = f.clone();
117 compose::<ClonableFnBrand, _, _, _>(ClonableFnBrand::new(
118 M::append::<ClonableFnBrand>,
119 ))(f)(a.to_owned())
120 })))(M::empty()))(fa)
121 })
122 }
123
124 /// Folds the structure by applying a function from right to left.
125 ///
126 /// The default implementation of `fold_right` is implemented in terms of [`fold_map`] using the [`Endomorphism` monoid][`crate::types::Endomorphism`] where:
127 ///
128 /// `((fold_right f) b) fa = ((fold_map f) fa) b`
129 ///
130 /// # Type Signature
131 ///
132 /// `forall f a b. Foldable f => (a -> b -> b) -> b -> f a -> b`
133 ///
134 /// # Parameters
135 ///
136 /// * `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.
137 /// * `b`: Initial value of type `B`.
138 /// * `fa`: A foldable structure containing values of type `A`.
139 ///
140 /// # Returns
141 ///
142 /// Final value of type `B` obtained from the folding operation.
143 ///
144 /// # Examples
145 ///
146 /// ```
147 /// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::fold_right};
148 /// use std::rc::Rc;
149 ///
150 /// assert_eq!(
151 /// fold_right::<RcFnBrand, VecBrand, _, _>(Rc::new(|item| Rc::new(move |carry| carry * 2 + item)))(0)(vec![1, 2, 3]),
152 /// 17
153 /// );
154 /// ```
155 fn fold_right<'a, ClonableFnBrand: 'a + ClonableFn, A: 'a + Clone, B: 'a + Clone>(
156 f: ApplyFn<'a, ClonableFnBrand, A, ApplyFn<'a, ClonableFnBrand, B, B>>
157 ) -> ApplyFn<'a, ClonableFnBrand, B, ApplyFn<'a, ClonableFnBrand, Apply0L1T<Self, A>, B>> {
158 ClonableFnBrand::new(move |b: B| {
159 ClonableFnBrand::new({
160 let f = f.clone();
161 move |fa| {
162 ((Self::fold_map::<ClonableFnBrand, A, Endomorphism<'a, ClonableFnBrand, B>>(
163 ClonableFnBrand::new({
164 let f = f.clone();
165 move |a| Endomorphism(f(a))
166 }),
167 )(fa))
168 .0)(b.to_owned())
169 }
170 })
171 })
172 }
173}
174
175/// Folds the structure by applying a function from left to right.
176///
177/// Free function version that dispatches to [the typeclass' associated function][`Foldable::fold_left`].
178///
179/// The default implementation of `fold_left` is implemented in terms of [`fold_right`], [`flip`], [`compose`] and [`identity`] where:
180///
181/// `((fold_left f) b) fa = (((fold_right (((compose (flip compose)) (flip f)))) identity) fa) b`
182///
183/// # Type Signature
184///
185/// `forall f a b. Foldable f => (b -> a -> b) -> b -> f a -> b`
186///
187/// # Parameters
188///
189/// * `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.
190/// * `b`: Initial value of type `B`.
191/// * `fa`: A foldable structure containing values of type `A`.
192///
193/// # Returns
194///
195/// Final value of type `B` obtained from the folding operation.
196///
197/// # Examples
198///
199/// ```
200/// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::fold_left};
201/// use std::rc::Rc;
202///
203/// assert_eq!(
204/// fold_left::<RcFnBrand, VecBrand, _, _>(Rc::new(|carry| Rc::new(move |item| carry * 2 + item)))(0)(vec![1, 2, 3]),
205/// 11
206/// );
207/// ```
208pub fn fold_left<
209 'a,
210 ClonableFnBrand: 'a + ClonableFn,
211 Brand: Foldable,
212 A: 'a + Clone,
213 B: 'a + Clone,
214>(
215 f: ApplyFn<'a, ClonableFnBrand, B, ApplyFn<'a, ClonableFnBrand, A, B>>
216) -> ApplyFn<'a, ClonableFnBrand, B, ApplyFn<'a, ClonableFnBrand, Apply0L1T<Brand, A>, B>> {
217 Brand::fold_left::<ClonableFnBrand, _, _>(f)
218}
219
220/// Maps values to a monoid and combines them.
221///
222/// Free function version that dispatches to [the typeclass' associated function][`Foldable::fold_map`].
223///
224/// The default implementation of `fold_map` is implemented in terms of [`fold_right`], [`compose`], [`append`][crate::functions::append] and [`empty`][crate::functions::empty] where:
225///
226/// `fold_map f = (fold_right ((compose append) f)) empty`
227///
228/// # Type Signature
229///
230/// `forall f a m. Foldable f, Monoid m => (a -> m) -> f a -> m`
231///
232/// # Parameters
233///
234/// * `f`: A function that converts from values into monoidal elements.
235/// * `fa`: A foldable structure containing values of type `A`.
236///
237/// # Returns
238///
239/// Final monoid obtained from the folding operation.
240///
241/// # Examples
242///
243/// ```
244/// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::{fold_map, identity}};
245/// use std::rc::Rc;
246///
247/// assert_eq!(
248/// fold_map::<RcFnBrand, VecBrand, _, String>(Rc::new(identity))(vec![
249/// "Hello, ".to_string(),
250/// "World!".to_string()
251/// ]),
252/// "Hello, World!"
253/// );
254/// ```
255pub fn fold_map<
256 'a,
257 ClonableFnBrand: 'a + ClonableFn,
258 Brand: Foldable,
259 A: 'a + Clone,
260 M: 'a + Monoid + Clone,
261>(
262 f: ApplyFn<'a, ClonableFnBrand, A, M>
263) -> ApplyFn<'a, ClonableFnBrand, Apply0L1T<Brand, A>, M> {
264 Brand::fold_map::<ClonableFnBrand, _, M>(f)
265}
266
267/// Folds the structure by applying a function from right to left.
268///
269/// Free function version that dispatches to [the typeclass' associated function][`Foldable::fold_right`].
270///
271/// The default implementation of `fold_right` is implemented in terms of [`fold_map`] using the [`Endomorphism` monoid][`crate::types::Endomorphism`] where:
272///
273/// `((fold_right f) b) fa = ((fold_map f) fa) b`
274///
275/// # Type Signature
276///
277/// `forall f a b. Foldable f => (a -> b -> b) -> b -> f a -> b`
278///
279/// # Parameters
280///
281/// * `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.
282/// * `b`: Initial value of type `B`.
283/// * `fa`: A foldable structure containing values of type `A`.
284///
285/// # Returns
286///
287/// Final value of type `B` obtained from the folding operation.
288///
289/// # Examples
290///
291/// ```
292/// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::fold_right};
293/// use std::rc::Rc;
294///
295/// assert_eq!(
296/// fold_right::<RcFnBrand, VecBrand, _, _>(Rc::new(|item| Rc::new(move |carry| carry * 2 + item)))(0)(vec![1, 2, 3]),
297/// 17
298/// );
299/// ```
300pub fn fold_right<
301 'a,
302 ClonableFnBrand: 'a + ClonableFn,
303 Brand: Foldable,
304 A: 'a + Clone,
305 B: 'a + Clone,
306>(
307 f: ApplyFn<'a, ClonableFnBrand, A, ApplyFn<'a, ClonableFnBrand, B, B>>
308) -> ApplyFn<'a, ClonableFnBrand, B, ApplyFn<'a, ClonableFnBrand, Apply0L1T<Brand, A>, B>> {
309 Brand::fold_right::<ClonableFnBrand, _, _>(f)
310}