Skip to main content

fp_library/classes/
par_functor.rs

1//! Data structures that can be mapped over in parallel.
2//!
3//! ### Examples
4//!
5//! ```
6//! use fp_library::{
7//! 	brands::*,
8//! 	functions::*,
9//! };
10//!
11//! let v = vec![1, 2, 3];
12//! let result: Vec<i32> = par_map::<VecBrand, _, _>(|x| x * 2, v);
13//! assert_eq!(result, vec![2, 4, 6]);
14//! ```
15
16#[fp_macros::document_module]
17mod inner {
18	use {
19		crate::kinds::*,
20		fp_macros::*,
21	};
22
23	/// A type class for data structures that can be mapped over in parallel.
24	///
25	/// `ParFunctor` is the parallel counterpart to [`Functor`](crate::classes::Functor).
26	/// Implementors define [`par_map`][ParFunctor::par_map] directly: there is no intermediate
27	/// `Vec` conversion imposed by the interface. Types backed by contiguous memory (e.g.,
28	/// [`VecBrand`](crate::brands::VecBrand)) can use rayon's parallel iterators directly;
29	/// other types may collect to `Vec` as an implementation detail.
30	///
31	/// ### Laws
32	///
33	/// `ParFunctor` instances must satisfy the same laws as `Functor`:
34	/// * Identity: `par_map(identity, fa) = fa`.
35	/// * Composition: `par_map(|a| f(g(a)), fa) = par_map(f, par_map(g, fa))`.
36	///
37	/// ### Thread Safety
38	///
39	/// All `par_*` functions require `A: Send`, `B: Send`, and closures to be `Send + Sync`.
40	/// These bounds apply even when the `rayon` feature is disabled, so that code compiles
41	/// identically in both configurations.
42	///
43	/// **Note: The `rayon` feature must be enabled to use actual parallel execution. Without
44	/// it, all `par_*` functions fall back to equivalent sequential operations.**
45	#[document_examples]
46	///
47	/// ParFunctor laws for [`Vec`]:
48	///
49	/// ```
50	/// use fp_library::{
51	/// 	brands::VecBrand,
52	/// 	functions::*,
53	/// };
54	///
55	/// let xs = vec![1, 2, 3];
56	///
57	/// // Identity: par_map(identity, fa) = fa
58	/// assert_eq!(par_map::<VecBrand, _, _>(identity, xs.clone()), xs);
59	///
60	/// // Composition: par_map(|a| f(g(a)), fa) = par_map(f, par_map(g, fa))
61	/// let f = |a: i32| a + 1;
62	/// let g = |a: i32| a * 2;
63	/// assert_eq!(
64	/// 	par_map::<VecBrand, _, _>(|a| f(g(a)), xs.clone()),
65	/// 	par_map::<VecBrand, _, _>(f, par_map::<VecBrand, _, _>(g, xs)),
66	/// );
67	/// ```
68	#[kind(type Of<'a, A: 'a>: 'a;)]
69	pub trait ParFunctor {
70		/// Maps a function over a data structure in parallel.
71		///
72		/// When the `rayon` feature is enabled, elements are processed across multiple threads.
73		/// Otherwise falls back to sequential mapping.
74		#[document_signature]
75		///
76		#[document_type_parameters(
77			"The lifetime of the elements.",
78			"The input element type.",
79			"The output element type."
80		)]
81		///
82		#[document_parameters(
83			"The function to apply to each element. Must be `Send + Sync`.",
84			"The data structure to map over."
85		)]
86		///
87		#[document_returns("A new data structure containing the mapped elements.")]
88		#[document_examples]
89		///
90		/// ```
91		/// use fp_library::{
92		/// 	brands::VecBrand,
93		/// 	classes::par_functor::ParFunctor,
94		/// };
95		///
96		/// let result = VecBrand::par_map(|x: i32| x * 2, vec![1, 2, 3]);
97		/// assert_eq!(result, vec![2, 4, 6]);
98		/// ```
99		fn par_map<'a, A: 'a + Send, B: 'a + Send>(
100			f: impl Fn(A) -> B + Send + Sync + 'a,
101			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
102		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>);
103	}
104
105	/// Maps a function over a data structure in parallel.
106	///
107	/// Applies `f` to each element independently. When the `rayon` feature is enabled,
108	/// elements are processed across multiple threads. Otherwise falls back to sequential mapping.
109	///
110	/// Free function version that dispatches to [`ParFunctor::par_map`].
111	#[document_signature]
112	///
113	#[document_type_parameters(
114		"The lifetime of the elements.",
115		"The brand of the collection.",
116		"The input element type.",
117		"The output element type."
118	)]
119	///
120	#[document_parameters(
121		"The function to apply to each element. Must be `Send + Sync`.",
122		"The collection to map over."
123	)]
124	///
125	#[document_returns("A new collection containing the mapped elements.")]
126	#[document_examples]
127	///
128	/// ```
129	/// use fp_library::{
130	/// 	brands::*,
131	/// 	functions::*,
132	/// };
133	///
134	/// let v = vec![1, 2, 3];
135	/// let result: Vec<i32> = par_map::<VecBrand, _, _>(|x| x * 2, v);
136	/// assert_eq!(result, vec![2, 4, 6]);
137	/// ```
138	pub fn par_map<'a, Brand: ParFunctor, A: 'a + Send, B: 'a + Send>(
139		f: impl Fn(A) -> B + Send + Sync + 'a,
140		fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
141	) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
142		Brand::par_map(f, fa)
143	}
144}
145
146pub use inner::*;