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}