fp_library/classes/
foldable.rs

1use crate::{
2	classes::{ClonableFn, Monoid, Semigroup, clonable_fn::ApplyClonableFn},
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: ApplyClonableFn<'a, ClonableFnBrand, B, ApplyClonableFn<'a, ClonableFnBrand, A, B>>
49	) -> ApplyClonableFn<
50		'a,
51		ClonableFnBrand,
52		B,
53		ApplyClonableFn<'a, ClonableFnBrand, Apply0L1T<Self, A>, B>,
54	> {
55		<ClonableFnBrand as ClonableFn>::new(move |b: B| {
56			<ClonableFnBrand as ClonableFn>::new({
57				let f = f.clone();
58				move |fa| {
59					(((Self::fold_right::<ClonableFnBrand, _, _>(compose::<
60						ClonableFnBrand,
61						_,
62						_,
63						_,
64					>(flip::<
65						ClonableFnBrand,
66						_,
67						_,
68						_,
69					>(
70						<ClonableFnBrand as ClonableFn>::new(compose::<ClonableFnBrand, _, _, _>),
71					))(flip::<
72						ClonableFnBrand,
73						_,
74						_,
75						_,
76					>(f.clone()))))(<ClonableFnBrand as ClonableFn>::new(identity)))(fa))(
77						b.to_owned()
78					)
79				}
80			})
81		})
82	}
83
84	/// Maps values to a monoid and combines them.
85	///
86	/// The default implementation of `fold_map` is implemented in terms of [`fold_right`], [`compose`], [`append`][crate::functions::append] and [`empty`][crate::functions::empty] where:
87	///
88	/// `fold_map f = (fold_right ((compose append) f)) empty`
89	///
90	/// # Type Signature
91	///
92	/// `forall a. Foldable f, Monoid m => (a -> m) -> f a -> m`
93	///
94	/// # Parameters
95	///
96	/// * `f`: A function that converts from values into monoidal elements.
97	/// * `fa`: A foldable structure containing values of type `A`.
98	///
99	/// # Returns
100	///
101	/// Final monoid obtained from the folding operation.
102	///
103	/// # Examples
104	///
105	/// ```
106	/// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::{fold_map, identity}};
107	/// use std::rc::Rc;
108	///
109	/// assert_eq!(
110	///     fold_map::<RcFnBrand, VecBrand, _, String>(Rc::new(identity))(vec![
111	///         "Hello, ".to_string(),
112	///         "World!".to_string()
113	///     ]),
114	///     "Hello, World!"
115	/// );
116	/// ```
117	fn fold_map<'a, ClonableFnBrand: 'a + ClonableFn, A: Clone, M: Monoid<'a> + Clone>(
118		f: ApplyClonableFn<'a, ClonableFnBrand, A, M>
119	) -> ApplyClonableFn<'a, ClonableFnBrand, Apply0L1T<Self, A>, M> {
120		<ClonableFnBrand as ClonableFn>::new(move |fa: Apply0L1T<Self, A>| {
121			((Self::fold_right::<'a, ClonableFnBrand, A, M>((compose::<
122				'a,
123				ClonableFnBrand,
124				A,
125				M,
126				_,
127			>(
128				<ClonableFnBrand as ClonableFn>::new::<'a, M, _>(
129					Semigroup::<'a>::append::<ClonableFnBrand>,
130				),
131			))(f.clone())))(M::empty()))(fa)
132		})
133	}
134
135	/// Folds the structure by applying a function from right to left.
136	///
137	/// The default implementation of `fold_right` is implemented in terms of [`fold_map`] using the [`Endofunction` monoid][`crate::types::Endofunction`] where:
138	///
139	/// `((fold_right f) b) fa = ((fold_map f) fa) b`
140	///
141	/// # Type Signature
142	///
143	/// `forall a b. Foldable f => (a -> b -> b) -> b -> f a -> b`
144	///
145	/// # Parameters
146	///
147	/// * `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.
148	/// * `b`: Initial value of type `B`.
149	/// * `fa`: A foldable structure containing values of type `A`.
150	///
151	/// # Returns
152	///
153	/// Final value of type `B` obtained from the folding operation.
154	///
155	/// # Examples
156	///
157	/// ```
158	/// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::fold_right};
159	/// use std::rc::Rc;
160	///
161	/// assert_eq!(
162	///     fold_right::<RcFnBrand, VecBrand, _, _>(Rc::new(|item| Rc::new(move |carry| carry * 2 + item)))(0)(vec![1, 2, 3]),
163	///     17
164	/// );
165	/// ```
166	fn fold_right<'a, ClonableFnBrand: 'a + ClonableFn, A: Clone, B: Clone>(
167		f: ApplyClonableFn<'a, ClonableFnBrand, A, ApplyClonableFn<'a, ClonableFnBrand, B, B>>
168	) -> ApplyClonableFn<
169		'a,
170		ClonableFnBrand,
171		B,
172		ApplyClonableFn<'a, ClonableFnBrand, Apply0L1T<Self, A>, B>,
173	> {
174		<ClonableFnBrand as ClonableFn>::new(move |b: B| {
175			let f = f.clone();
176			<ClonableFnBrand as ClonableFn>::new(move |fa: Apply0L1T<Self, A>| {
177				(Self::fold_map::<'a, ClonableFnBrand, _, _>(<ClonableFnBrand as ClonableFn>::new(
178					{
179						let f = f.clone();
180						move |a: A| Endofunction::<'a, ClonableFnBrand, _>::new(f(a))
181					},
182				))(fa)
183				.0)(b.clone())
184			})
185		})
186	}
187}
188
189/// Folds the structure by applying a function from left to right.
190///
191/// Free function version that dispatches to [the type class' associated function][`Foldable::fold_left`].
192///
193/// The default implementation of `fold_left` is implemented in terms of [`fold_right`], [`flip`], [`compose`] and [`identity`] where:
194///
195/// `((fold_left f) b) fa = (((fold_right (((compose (flip compose)) (flip f)))) identity) fa) b`
196///
197/// # Type Signature
198///
199/// `forall a b. Foldable f => (b -> a -> b) -> b -> f a -> b`
200///
201/// # Parameters
202///
203/// * `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.
204/// * `b`: Initial value of type `B`.
205/// * `fa`: A foldable structure containing values of type `A`.
206///
207/// # Returns
208///
209/// Final value of type `B` obtained from the folding operation.
210///
211/// # Examples
212///
213/// ```
214/// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::fold_left};
215/// use std::rc::Rc;
216///
217/// assert_eq!(
218///     fold_left::<RcFnBrand, VecBrand, _, _>(Rc::new(|carry| Rc::new(move |item| carry * 2 + item)))(0)(vec![1, 2, 3]),
219///     11
220/// );
221/// ```
222pub fn fold_left<'a, ClonableFnBrand: 'a + ClonableFn, Brand: Foldable, A: Clone, B: Clone>(
223	f: ApplyClonableFn<'a, ClonableFnBrand, B, ApplyClonableFn<'a, ClonableFnBrand, A, B>>
224) -> ApplyClonableFn<
225	'a,
226	ClonableFnBrand,
227	B,
228	ApplyClonableFn<'a, ClonableFnBrand, Apply0L1T<Brand, A>, B>,
229> {
230	Brand::fold_left::<ClonableFnBrand, _, _>(f)
231}
232
233/// Maps values to a monoid and combines them.
234///
235/// Free function version that dispatches to [the type class' associated function][`Foldable::fold_map`].
236///
237/// The default implementation of `fold_map` is implemented in terms of [`fold_right`], [`compose`], [`append`][crate::functions::append] and [`empty`][crate::functions::empty] where:
238///
239/// `fold_map f = (fold_right ((compose append) f)) empty`
240///
241/// # Type Signature
242///
243/// `forall a. Foldable f, Monoid m => (a -> m) -> f a -> m`
244///
245/// # Parameters
246///
247/// * `f`: A function that converts from values into monoidal elements.
248/// * `fa`: A foldable structure containing values of type `A`.
249///
250/// # Returns
251///
252/// Final monoid obtained from the folding operation.
253///
254/// # Examples
255///
256/// ```
257/// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::{fold_map, identity}};
258/// use std::rc::Rc;
259///
260/// assert_eq!(
261///     fold_map::<RcFnBrand, VecBrand, _, String>(Rc::new(identity))(vec![
262///         "Hello, ".to_string(),
263///         "World!".to_string()
264///     ]),
265///     "Hello, World!"
266/// );
267/// ```
268pub fn fold_map<
269	'a,
270	ClonableFnBrand: 'a + ClonableFn,
271	Brand: Foldable,
272	A: Clone,
273	M: Monoid<'a> + Clone,
274>(
275	f: ApplyClonableFn<'a, ClonableFnBrand, A, M>
276) -> ApplyClonableFn<'a, ClonableFnBrand, Apply0L1T<Brand, A>, M> {
277	Brand::fold_map::<ClonableFnBrand, _, M>(f)
278}
279
280/// Folds the structure by applying a function from right to left.
281///
282/// Free function version that dispatches to [the type class' associated function][`Foldable::fold_right`].
283///
284/// The default implementation of `fold_right` is implemented in terms of [`fold_map`] using the [`Endomorphism` monoid][`crate::types::Endomorphism`] where:
285///
286/// `((fold_right f) b) fa = ((fold_map f) fa) b`
287///
288/// # Type Signature
289///
290/// `forall a b. Foldable f => (a -> b -> b) -> b -> f a -> b`
291///
292/// # Parameters
293///
294/// * `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.
295/// * `b`: Initial value of type `B`.
296/// * `fa`: A foldable structure containing values of type `A`.
297///
298/// # Returns
299///
300/// Final value of type `B` obtained from the folding operation.
301///
302/// # Examples
303///
304/// ```
305/// use fp_library::{brands::{VecBrand, RcFnBrand}, functions::fold_right};
306/// use std::rc::Rc;
307///
308/// assert_eq!(
309///     fold_right::<RcFnBrand, VecBrand, _, _>(Rc::new(|item| Rc::new(move |carry| carry * 2 + item)))(0)(vec![1, 2, 3]),
310///     17
311/// );
312/// ```
313pub fn fold_right<'a, ClonableFnBrand: 'a + ClonableFn, Brand: Foldable, A: Clone, B: Clone>(
314	f: ApplyClonableFn<'a, ClonableFnBrand, A, ApplyClonableFn<'a, ClonableFnBrand, B, B>>
315) -> ApplyClonableFn<
316	'a,
317	ClonableFnBrand,
318	B,
319	ApplyClonableFn<'a, ClonableFnBrand, Apply0L1T<Brand, A>, B>,
320> {
321	Brand::fold_right::<ClonableFnBrand, _, _>(f)
322}