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