fp_library/classes/par_filterable.rs
1//! Data structures that can be filtered and filter-mapped in parallel.
2//!
3//! ### Examples
4//!
5//! ```
6//! use fp_library::{
7//! brands::*,
8//! functions::*,
9//! };
10//!
11//! let v = vec![1, 2, 3, 4, 5];
12//! let result: Vec<i32> =
13//! par_filter_map::<VecBrand, _, _>(|x: i32| if x % 2 == 0 { Some(x * 10) } else { None }, v);
14//! assert_eq!(result, vec![20, 40]);
15//! ```
16
17#[fp_macros::document_module]
18mod inner {
19 use {
20 crate::{
21 classes::{
22 ParCompactable,
23 ParFunctor,
24 },
25 kinds::*,
26 },
27 fp_macros::*,
28 };
29
30 /// A type class for data structures that can be filtered and filter-mapped in parallel.
31 ///
32 /// `ParFilterable` is the parallel counterpart to [`Filterable`](crate::classes::Filterable),
33 /// extending [`ParFunctor`] and [`ParCompactable`].
34 ///
35 /// All methods have default implementations based on [`par_map`][ParFunctor::par_map] and
36 /// [`par_compact`][ParCompactable::par_compact]. However, it is recommended to override
37 /// [`ParFilterable::par_filter_map`] and [`ParFilterable::par_filter`] with single-pass
38 /// implementations to avoid the intermediate allocation created by the default.
39 ///
40 /// * If [`ParFilterable::par_filter_map`] is overridden, [`ParFilterable::par_filter`] is
41 /// automatically derived from it (no [`Clone`] bound required).
42 ///
43 /// ### Laws
44 ///
45 /// `ParFilterable` instances must satisfy the same laws as `Filterable`:
46 /// * Identity: `par_filter_map(Some, fa) = fa`.
47 /// * Composition: `par_filter_map(|a| r(a).and_then(l), fa) = par_filter_map(l, par_filter_map(r, fa))`.
48 /// * Consistency: `par_filter(p, fa) = par_filter_map(|a| if p(&a) { Some(a) } else { None }, fa)`.
49 ///
50 /// ### Thread Safety
51 ///
52 /// All `par_*` functions require `A: Send`, `B: Send`, and closures to be `Send + Sync`.
53 /// These bounds apply even when the `rayon` feature is disabled, so that code compiles
54 /// identically in both configurations.
55 ///
56 /// **Note: The `rayon` feature must be enabled to use actual parallel execution. Without
57 /// it, all `par_*` functions fall back to equivalent sequential operations.**
58 #[document_examples]
59 ///
60 /// ```
61 /// use fp_library::{
62 /// brands::VecBrand,
63 /// functions::*,
64 /// };
65 ///
66 /// let v = vec![1, 2, 3, 4, 5];
67 /// let mapped: Vec<i32> = par_filter_map::<VecBrand, _, _>(
68 /// |x: i32| if x % 2 == 0 { Some(x * 10) } else { None },
69 /// v.clone(),
70 /// );
71 /// assert_eq!(mapped, vec![20, 40]);
72 ///
73 /// let filtered: Vec<i32> = par_filter::<VecBrand, _>(|x: &i32| x % 2 == 0, v);
74 /// assert_eq!(filtered, vec![2, 4]);
75 /// ```
76 pub trait ParFilterable: ParFunctor + ParCompactable {
77 /// Maps and filters a data structure in parallel, discarding elements for which `f` returns
78 /// [`None`].
79 ///
80 /// The default implementation uses [`par_map`][ParFunctor::par_map] followed by
81 /// [`par_compact`][ParCompactable::par_compact]. Override this method with a single-pass
82 /// implementation for better performance.
83 #[document_signature]
84 ///
85 #[document_type_parameters(
86 "The lifetime of the elements.",
87 "The input element type.",
88 "The output element type."
89 )]
90 ///
91 #[document_parameters(
92 "The function to apply to each element, returning an [`Option`]. Must be `Send + Sync`.",
93 "The data structure to filter and map."
94 )]
95 ///
96 #[document_returns(
97 "A new data structure containing only the values where `f` returned [`Some`]."
98 )]
99 #[document_examples]
100 ///
101 /// ```
102 /// use fp_library::{
103 /// brands::VecBrand,
104 /// classes::par_filterable::ParFilterable,
105 /// };
106 ///
107 /// let result = VecBrand::par_filter_map(
108 /// |x: i32| if x % 2 == 0 { Some(x * 10) } else { None },
109 /// vec![1, 2, 3, 4, 5],
110 /// );
111 /// assert_eq!(result, vec![20, 40]);
112 /// ```
113 fn par_filter_map<'a, A: 'a + Send, B: 'a + Send>(
114 f: impl Fn(A) -> Option<B> + Send + Sync + 'a,
115 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
116 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
117 Self::par_compact::<B>(Self::par_map::<A, Option<B>>(f, fa))
118 }
119
120 /// Filters a data structure in parallel, retaining only elements satisfying `f`.
121 ///
122 /// The default implementation derives from [`par_filter_map`][Self::par_filter_map].
123 /// No [`Clone`] bound is required: ownership of each element is passed to the closure,
124 /// which either returns `Some(a)` (retain) or `None` (discard).
125 ///
126 /// Override this method with a single-pass implementation for better performance.
127 #[document_signature]
128 ///
129 #[document_type_parameters("The lifetime of the elements.", "The element type.")]
130 ///
131 #[document_parameters(
132 "The predicate. Must be `Send + Sync`.",
133 "The data structure to filter."
134 )]
135 ///
136 #[document_returns("A new data structure containing only the elements that satisfy `f`.")]
137 #[document_examples]
138 ///
139 /// ```
140 /// use fp_library::{
141 /// brands::VecBrand,
142 /// classes::par_filterable::ParFilterable,
143 /// };
144 ///
145 /// let result = VecBrand::par_filter(|x: &i32| x % 2 == 0, vec![1, 2, 3, 4, 5]);
146 /// assert_eq!(result, vec![2, 4]);
147 /// ```
148 fn par_filter<'a, A: 'a + Send>(
149 f: impl Fn(&A) -> bool + Send + Sync + 'a,
150 fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
151 ) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>) {
152 Self::par_filter_map::<A, A>(move |a| if f(&a) { Some(a) } else { None }, fa)
153 }
154 }
155
156 /// Maps and filters a data structure in parallel, discarding elements for which `f` returns
157 /// [`None`].
158 ///
159 /// Free function version that dispatches to [`ParFilterable::par_filter_map`].
160 #[document_signature]
161 ///
162 #[document_type_parameters(
163 "The lifetime of the elements.",
164 "The brand of the collection.",
165 "The input element type.",
166 "The output element type."
167 )]
168 ///
169 #[document_parameters(
170 "The function to apply to each element, returning an [`Option`]. Must be `Send + Sync`.",
171 "The data structure to filter and map."
172 )]
173 ///
174 #[document_returns("A new collection containing only the values where `f` returned [`Some`].")]
175 #[document_examples]
176 ///
177 /// ```
178 /// use fp_library::{
179 /// brands::*,
180 /// functions::*,
181 /// };
182 ///
183 /// let v = vec![1, 2, 3, 4, 5];
184 /// let result: Vec<i32> =
185 /// par_filter_map::<VecBrand, _, _>(|x: i32| if x % 2 == 0 { Some(x * 10) } else { None }, v);
186 /// assert_eq!(result, vec![20, 40]);
187 /// ```
188 pub fn par_filter_map<'a, Brand: ParFilterable, A: 'a + Send, B: 'a + Send>(
189 f: impl Fn(A) -> Option<B> + Send + Sync + 'a,
190 fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
191 ) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
192 Brand::par_filter_map(f, fa)
193 }
194
195 /// Filters a data structure in parallel, retaining only elements satisfying `f`.
196 ///
197 /// Free function version that dispatches to [`ParFilterable::par_filter`].
198 #[document_signature]
199 ///
200 #[document_type_parameters(
201 "The lifetime of the elements.",
202 "The brand of the collection.",
203 "The element type."
204 )]
205 ///
206 #[document_parameters("The predicate. Must be `Send + Sync`.", "The data structure to filter.")]
207 ///
208 #[document_returns("A new collection containing only the elements satisfying `f`.")]
209 #[document_examples]
210 ///
211 /// ```
212 /// use fp_library::{
213 /// brands::*,
214 /// functions::*,
215 /// };
216 ///
217 /// let v = vec![1, 2, 3, 4, 5];
218 /// let result: Vec<i32> = par_filter::<VecBrand, _>(|x: &i32| x % 2 == 0, v);
219 /// assert_eq!(result, vec![2, 4]);
220 /// ```
221 pub fn par_filter<'a, Brand: ParFilterable, A: 'a + Send>(
222 f: impl Fn(&A) -> bool + Send + Sync + 'a,
223 fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
224 ) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>) {
225 Brand::par_filter(f, fa)
226 }
227}
228
229pub use inner::*;