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}