fp_library/classes/
par_foldable.rs

1//! Parallel folding operations.
2//!
3//! This module defines the [`ParFoldable`] trait, which provides parallel versions of `Foldable` operations.
4//!
5//! **Note: The `rayon` feature must be enabled to use parallel iteration.**
6//!
7//! ### Examples
8//!
9//! ```
10//! use fp_library::{brands::*, functions::*};
11//!
12//! let v = vec![1, 2, 3, 4, 5];
13//! let f = send_clonable_fn_new::<ArcFnBrand, _, _>(|x: i32| x.to_string());
14//! let result: String = par_fold_map::<ArcFnBrand, VecBrand, _, _>(f, v);
15//! assert_eq!(result, "12345");
16//! ```
17
18use super::{foldable::Foldable, monoid::Monoid, send_clonable_fn::SendClonableFn};
19use crate::{Apply, kinds::*, types::SendEndofunction};
20
21/// A type class for structures that can be folded in parallel.
22///
23/// This trait provides parallel versions of `Foldable` operations that require
24/// `Send + Sync` bounds on elements and functions. It uses the branded
25/// `SendOf` function type to maintain the library's HKT abstraction.
26///
27/// **Note: The `rayon` feature must be enabled to use parallel iteration.**
28///
29/// ### Minimal Implementation
30///
31/// A minimal implementation requires [`ParFoldable::par_fold_map`].
32///
33/// ### Thread Safety
34///
35/// All operations in this trait are designed to be safe for parallel execution:
36/// - Element type `A` must be `Send + Sync`
37/// - Accumulator/result types must be `Send + Sync`
38/// - Functions are wrapped in `FnBrand::SendOf` which guarantees `Send + Sync`
39///
40/// ### Why is `FnBrand` a Trait-Level Parameter?
41///
42/// Unlike [`Foldable`] where `FnBrand` is a method-level generic parameter
43/// and functions are raw `Fn` types wrapped internally, `ParFoldable` takes `FnBrand`
44/// at the trait level. This design choice is motivated by:
45///
46/// 1. **Thread-safe function values as first-class HKT types**: Function parameters
47///    like `func` in [`ParFoldable::par_fold_map`] are HKT-applied types via
48///    `Apply!(brand: FnBrand, kind: SendClonableFn, output: SendOf, ...)`. This allows
49///    the type system to enforce thread-safety at the API boundary.
50///
51/// 2. **Guaranteed `Send + Sync` bounds**: The `output: SendOf` in the `Apply!` macro
52///    ensures the function type carries `Send + Sync` bounds essential for parallel
53///    execution, rather than relying on runtime checks.
54///
55/// 3. **Default implementation requirements**: The default [`ParFoldable::par_fold_right`]
56///    implementation needs to call `<FnBrand as SendClonableFn>::send_clonable_fn_new(...)` to
57///    create new wrapped functions. Having `FnBrand` at the trait level makes it
58///    available throughout the implementation.
59///
60/// 4. **Multiple implementations per data structure**: With trait-level parameterization,
61///    a type can implement `ParFoldable<ArcFnBrand>` and potentially other function
62///    brands, allowing callers to choose the appropriate thread-safe function wrapper.
63///
64/// ### Examples
65///
66/// ```
67/// use fp_library::{brands::*, functions::*};
68///
69/// let v = vec![1, 2, 3, 4, 5];
70/// let f = send_clonable_fn_new::<ArcFnBrand, _, _>(|x: i32| x.to_string());
71/// let result: String = par_fold_map::<ArcFnBrand, VecBrand, _, _>(f, v);
72/// assert_eq!(result, "12345");
73/// ```
74pub trait ParFoldable<FnBrand: SendClonableFn>: Foldable {
75	/// Parallel version of fold_map.
76	///
77	/// Maps each element to a monoid value using `func`, then combines all values using the monoid's `append` operation. The mapping operations may be executed in parallel.
78	///
79	/// ### Type Signature
80	///
81	/// `forall fn_brand t m a. (ParFoldable t, Monoid m, Send m, Sync m) => (fn_brand a m, t a) -> m`
82	///
83	/// ### Type Parameters
84	///
85	/// * `FnBrand`: The brand of thread-safe function to use (must implement `SendClonableFn`).
86	/// * `A`: The element type (must be `Send + Sync`).
87	/// * `M`: The monoid type (must be `Send + Sync`).
88	///
89	/// ### Parameters
90	///
91	/// * `func`: The thread-safe function to map each element to a monoid`.
92	/// * `fa`: The foldable structure.
93	///
94	/// ### Returns
95	///
96	/// The combined monoid value
97	///
98	/// ### Examples
99	///
100	/// ```
101	/// use fp_library::{brands::*, functions::*};
102	///
103	/// let v = vec![1, 2, 3, 4, 5];
104	/// let f = send_clonable_fn_new::<ArcFnBrand, _, _>(|x: i32| x.to_string());
105	/// let result: String = par_fold_map::<ArcFnBrand, VecBrand, _, _>(f, v);
106	/// assert_eq!(result, "12345");
107	/// ```
108	fn par_fold_map<'a, M, A>(
109		func: <FnBrand as SendClonableFn>::SendOf<'a, A, M>,
110		fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
111	) -> M
112	where
113		A: 'a + Clone + Send + Sync,
114		M: Monoid + Send + Sync + 'a;
115
116	/// Parallel version of fold_right.
117	///
118	/// Folds the structure by applying a function from right to left, potentially in parallel.
119	///
120	/// ### Type Signature
121	///
122	/// `forall fn_brand t b a. ParFoldable t => (fn_brand (a, b) b, b, t a) -> b`
123	///
124	/// ### Type Parameters
125	///
126	/// * `FnBrand`: The brand of thread-safe function to use
127	/// * `A`: The element type (must be `Send + Sync`)
128	/// * `B`: The accumulator type (must be `Send + Sync`)
129	///
130	/// ### Parameters
131	///
132	/// * `func`: The thread-safe function to apply to each element and the accumulator.
133	/// * `initial`: The initial value of the accumulator.
134	/// * `fa`: The structure to fold.
135	///
136	/// ### Returns
137	///
138	/// The final accumulator value
139	///
140	/// ### Examples
141	///
142	/// ```
143	/// use fp_library::{brands::*, functions::*};
144	///
145	/// let v = vec![1, 2, 3, 4, 5];
146	/// let f = send_clonable_fn_new::<ArcFnBrand, _, _>(|(a, b)| a + b);
147	/// let sum = par_fold_right::<ArcFnBrand, VecBrand, _, _>(f, 10, v);
148	/// assert_eq!(sum, 25);
149	/// ```
150	fn par_fold_right<'a, B, A>(
151		func: <FnBrand as SendClonableFn>::SendOf<'a, (A, B), B>,
152		init: B,
153		fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
154	) -> B
155	where
156		A: 'a + Clone + Send + Sync,
157		B: Send + Sync + 'a,
158		FnBrand: 'a,
159	{
160		let f_clone = func.clone();
161		let endo = Self::par_fold_map(
162			<FnBrand as SendClonableFn>::send_clonable_fn_new(move |a: A| {
163				let f_inner = f_clone.clone();
164				SendEndofunction::<FnBrand, B>::new(
165					<FnBrand as SendClonableFn>::send_clonable_fn_new(move |b: B| {
166						f_inner((a.clone(), b))
167					}),
168				)
169			}),
170			fa,
171		);
172		endo.0(init)
173	}
174}
175
176/// Parallel fold_map operation.
177///
178/// Free function version that dispatches to [the type class' associated function][`ParFoldable::par_fold_map`].
179///
180/// ### Type Signature
181///
182/// `forall fn_brand t m a. (ParFoldable t, Monoid m, Send m, Sync m) => (fn_brand a m, t a) -> m`
183///
184/// ### Type Parameters
185///
186/// * `FnBrand`: The brand of thread-safe function to use (must implement `SendClonableFn`).
187/// * `Brand`: The brand of the foldable structure.
188/// * `M`: The monoid type (must be `Send + Sync`).
189/// * `A`: The element type (must be `Send + Sync`).
190///
191/// ### Parameters
192///
193/// * `func`: The thread-safe function to map each element to a monoid.
194/// * `fa`: The structure to fold.
195///
196/// ### Returns
197///
198/// The combined monoid value.
199///
200/// ### Examples
201///
202/// ```
203/// use fp_library::{brands::*, functions::*};
204///
205/// let v = vec![1, 2, 3, 4, 5];
206/// let f = send_clonable_fn_new::<ArcFnBrand, _, _>(|x: i32| x.to_string());
207/// let result: String = par_fold_map::<ArcFnBrand, VecBrand, _, _>(f, v);
208/// assert_eq!(result, "12345");
209/// ```
210pub fn par_fold_map<'a, FnBrand, Brand, M, A>(
211	func: <FnBrand as SendClonableFn>::SendOf<'a, A, M>,
212	fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
213) -> M
214where
215	FnBrand: SendClonableFn,
216	Brand: ParFoldable<FnBrand>,
217	A: 'a + Clone + Send + Sync,
218	M: Monoid + Send + Sync + 'a,
219{
220	Brand::par_fold_map::<M, A>(func, fa)
221}
222
223/// Parallel fold_right operation.
224///
225/// Free function version that dispatches to [the type class' associated function][`ParFoldable::par_fold_right`].
226///
227/// ### Type Signature
228///
229/// `forall fn_brand t b a. ParFoldable t => (fn_brand (a, b) b, b, t a) -> b`
230///
231/// ### Type Parameters
232///
233/// * `FnBrand`: The brand of thread-safe function to use.
234/// * `Brand`: The brand of the foldable structure.
235/// * `B`: The accumulator type (must be `Send + Sync`).
236/// * `A`: The element type (must be `Send + Sync`).
237///
238/// ### Parameters
239///
240/// * `func`: The thread-safe function to apply to each element and the accumulator.
241/// * `init`: The initial value of the accumulator.
242/// * `fa`: The structure to fold.
243///
244/// ### Returns
245///
246/// The final accumulator value.
247///
248/// ### Examples
249///
250/// ```
251/// use fp_library::{brands::*, functions::*};
252///
253/// let v = vec![1, 2, 3, 4, 5];
254/// let f = send_clonable_fn_new::<ArcFnBrand, _, _>(|(a, b)| a + b);
255/// let sum = par_fold_right::<ArcFnBrand, VecBrand, _, _>(f, 10, v);
256/// assert_eq!(sum, 25);
257/// ```
258pub fn par_fold_right<'a, FnBrand, Brand, B, A>(
259	func: <FnBrand as SendClonableFn>::SendOf<'a, (A, B), B>,
260	init: B,
261	fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
262) -> B
263where
264	FnBrand: SendClonableFn,
265	Brand: ParFoldable<FnBrand>,
266	A: 'a + Clone + Send + Sync,
267	B: Send + Sync + 'a,
268	FnBrand: 'a,
269{
270	Brand::par_fold_right::<B, A>(func, init, fa)
271}