Skip to main content

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::*;