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