fp_library/classes/
foldable.rs

1use crate::{
2	classes::{ClonableFn, Monoid, Semigroup, clonable_fn::ApplyFn},
3	functions::{compose, flip, identity},
4	hkt::{Apply0L1T, Kind0L1T},
5	types::Endofunction,
6};
7
8/// A type class 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 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: Clone, B: 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 a. 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: Clone, M: Monoid<'a> + Clone>(
111		f: ApplyFn<'a, ClonableFnBrand, A, M>
112	) -> ApplyFn<'a, ClonableFnBrand, Apply0L1T<Self, A>, M> {
113		ClonableFnBrand::new(move |fa: Apply0L1T<Self, A>| {
114			((Self::fold_right::<'a, ClonableFnBrand, A, M>((compose::<
115				'a,
116				ClonableFnBrand,
117				A,
118				M,
119				_,
120			>(ClonableFnBrand::new::<
121				'a,
122				M,
123				_,
124			>(
125				Semigroup::<'a>::append::<ClonableFnBrand>,
126			)))(f.clone())))(M::empty()))(fa)
127		})
128	}
129
130	/// Folds the structure by applying a function from right to left.
131	///
132	/// The default implementation of `fold_right` is implemented in terms of [`fold_map`] using the [`Endofunction` monoid][`crate::types::Endofunction`] where:
133	///
134	/// `((fold_right f) b) fa = ((fold_map f) fa) b`
135	///
136	/// # Type Signature
137	///
138	/// `forall a b. Foldable f => (a -> b -> b) -> b -> f a -> b`
139	///
140	/// # Parameters
141	///
142	/// * `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.
143	/// * `b`: Initial value of type `B`.
144	/// * `fa`: A foldable structure containing values of type `A`.
145	///
146	/// # Returns
147	///
148	/// Final value of type `B` obtained from the folding operation.
149	///
150	/// # Examples
151	///
152	/// ```
153	/// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::fold_right};
154	/// use std::rc::Rc;
155	///
156	/// assert_eq!(
157	///     fold_right::<RcFnBrand, VecBrand, _, _>(Rc::new(|item| Rc::new(move |carry| carry * 2 + item)))(0)(vec![1, 2, 3]),
158	///     17
159	/// );
160	/// ```
161	fn fold_right<'a, ClonableFnBrand: 'a + ClonableFn, A: Clone, B: Clone>(
162		f: ApplyFn<'a, ClonableFnBrand, A, ApplyFn<'a, ClonableFnBrand, B, B>>
163	) -> ApplyFn<'a, ClonableFnBrand, B, ApplyFn<'a, ClonableFnBrand, Apply0L1T<Self, A>, B>> {
164		ClonableFnBrand::new(move |b: B| {
165			let f = f.clone();
166			ClonableFnBrand::new(move |fa: Apply0L1T<Self, A>| {
167				(Self::fold_map::<'a, ClonableFnBrand, _, _>(ClonableFnBrand::new({
168					let f = f.clone();
169					move |a: A| Endofunction::<'a, ClonableFnBrand, _>::new(f(a))
170				}))(fa)
171				.0)(b.clone())
172			})
173		})
174	}
175}
176
177/// Folds the structure by applying a function from left to right.
178///
179/// Free function version that dispatches to [the type class' associated function][`Foldable::fold_left`].
180///
181/// The default implementation of `fold_left` is implemented in terms of [`fold_right`], [`flip`], [`compose`] and [`identity`] where:
182///
183/// `((fold_left f) b) fa = (((fold_right (((compose (flip compose)) (flip f)))) identity) fa) b`
184///
185/// # Type Signature
186///
187/// `forall a b. Foldable f => (b -> a -> b) -> b -> f a -> b`
188///
189/// # Parameters
190///
191/// * `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.
192/// * `b`: Initial value of type `B`.
193/// * `fa`: A foldable structure containing values of type `A`.
194///
195/// # Returns
196///
197/// Final value of type `B` obtained from the folding operation.
198///
199/// # Examples
200///
201/// ```
202/// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::fold_left};
203/// use std::rc::Rc;
204///
205/// assert_eq!(
206///     fold_left::<RcFnBrand, VecBrand, _, _>(Rc::new(|carry| Rc::new(move |item| carry * 2 + item)))(0)(vec![1, 2, 3]),
207///     11
208/// );
209/// ```
210pub fn fold_left<'a, ClonableFnBrand: 'a + ClonableFn, Brand: Foldable, A: Clone, B: Clone>(
211	f: ApplyFn<'a, ClonableFnBrand, B, ApplyFn<'a, ClonableFnBrand, A, B>>
212) -> ApplyFn<'a, ClonableFnBrand, B, ApplyFn<'a, ClonableFnBrand, Apply0L1T<Brand, A>, B>> {
213	Brand::fold_left::<ClonableFnBrand, _, _>(f)
214}
215
216/// Maps values to a monoid and combines them.
217///
218/// Free function version that dispatches to [the type class' associated function][`Foldable::fold_map`].
219///
220/// The default implementation of `fold_map` is implemented in terms of [`fold_right`], [`compose`], [`append`][crate::functions::append] and [`empty`][crate::functions::empty] where:
221///
222/// `fold_map f = (fold_right ((compose append) f)) empty`
223///
224/// # Type Signature
225///
226/// `forall a. Foldable f, Monoid m => (a -> m) -> f a -> m`
227///
228/// # Parameters
229///
230/// * `f`: A function that converts from values into monoidal elements.
231/// * `fa`: A foldable structure containing values of type `A`.
232///
233/// # Returns
234///
235/// Final monoid obtained from the folding operation.
236///
237/// # Examples
238///
239/// ```
240/// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::{fold_map, identity}};
241/// use std::rc::Rc;
242///
243/// assert_eq!(
244///     fold_map::<RcFnBrand, VecBrand, _, String>(Rc::new(identity))(vec![
245///         "Hello, ".to_string(),
246///         "World!".to_string()
247///     ]),
248///     "Hello, World!"
249/// );
250/// ```
251pub fn fold_map<
252	'a,
253	ClonableFnBrand: 'a + ClonableFn,
254	Brand: Foldable,
255	A: Clone,
256	M: Monoid<'a> + Clone,
257>(
258	f: ApplyFn<'a, ClonableFnBrand, A, M>
259) -> ApplyFn<'a, ClonableFnBrand, Apply0L1T<Brand, A>, M> {
260	Brand::fold_map::<ClonableFnBrand, _, M>(f)
261}
262
263/// Folds the structure by applying a function from right to left.
264///
265/// Free function version that dispatches to [the type class' associated function][`Foldable::fold_right`].
266///
267/// The default implementation of `fold_right` is implemented in terms of [`fold_map`] using the [`Endomorphism` monoid][`crate::types::Endomorphism`] where:
268///
269/// `((fold_right f) b) fa = ((fold_map f) fa) b`
270///
271/// # Type Signature
272///
273/// `forall a b. Foldable f => (a -> b -> b) -> b -> f a -> b`
274///
275/// # Parameters
276///
277/// * `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.
278/// * `b`: Initial value of type `B`.
279/// * `fa`: A foldable structure containing values of type `A`.
280///
281/// # Returns
282///
283/// Final value of type `B` obtained from the folding operation.
284///
285/// # Examples
286///
287/// ```
288/// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::fold_right};
289/// use std::rc::Rc;
290///
291/// assert_eq!(
292///     fold_right::<RcFnBrand, VecBrand, _, _>(Rc::new(|item| Rc::new(move |carry| carry * 2 + item)))(0)(vec![1, 2, 3]),
293///     17
294/// );
295/// ```
296pub fn fold_right<'a, ClonableFnBrand: 'a + ClonableFn, Brand: Foldable, A: Clone, B: Clone>(
297	f: ApplyFn<'a, ClonableFnBrand, A, ApplyFn<'a, ClonableFnBrand, B, B>>
298) -> ApplyFn<'a, ClonableFnBrand, B, ApplyFn<'a, ClonableFnBrand, Apply0L1T<Brand, A>, B>> {
299	Brand::fold_right::<ClonableFnBrand, _, _>(f)
300}