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