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