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}