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}