Skip to main content

fp_library/types/
send_endofunction.rs

1//! Thread-safe wrapper for endofunctions with [`Semigroup`](crate::classes::Semigroup) and [`Monoid`](crate::classes::Monoid) instances.
2//!
3//! The `Send + Sync` counterpart to [`Endofunction`](crate::types::Endofunction), wrapping functions that can be safely shared across threads.
4
5#[fp_macros::document_module]
6mod inner {
7	use crate::{
8		classes::{Monoid, Semigroup, SendCloneableFn},
9		functions::identity,
10	};
11	use fp_macros::{document_fields, document_parameters, document_type_parameters};
12	use std::{
13		fmt::{self, Debug, Formatter},
14		hash::Hash,
15	};
16
17	/// A thread-safe wrapper for endofunctions (functions from a set to the same set) that enables monoidal operations.
18	///
19	/// `SendEndofunction a` represents a function `a -> a` that is `Send + Sync`.
20	///
21	/// It exists to provide a monoid instance where:
22	///
23	/// * The binary operation [append][Semigroup::append] is [function composition][crate::functions::compose].
24	/// * The identity element [empty][Monoid::empty] is the [identity function][crate::functions::identity].
25	///
26	/// The wrapped function can be accessed directly via the [`.0` field][SendEndofunction#structfield.0].
27	///
28	/// ### Type Parameters
29	///
30	#[document_type_parameters(
31		"The lifetime of the function and its captured data.",
32		"The brand of the thread-safe cloneable function wrapper.",
33		"The input and output type of the function."
34	)]
35	///
36	/// ### Fields
37	///
38	#[document_fields("The wrapped thread-safe function.")]
39	///
40	/// ### Examples
41	///
42	/// ```
43	/// use fp_library::{brands::*, functions::*, types::*};
44	///
45	/// let f = SendEndofunction::<ArcFnBrand, _>::new(send_cloneable_fn_new::<ArcFnBrand, _, _>(|x: i32| x * 2));
46	/// assert_eq!(f.0(5), 10);
47	/// ```
48	pub struct SendEndofunction<'a, FnBrand: SendCloneableFn, A>(
49		pub <FnBrand as SendCloneableFn>::SendOf<'a, A, A>,
50	);
51
52	/// ### Type Parameters
53	///
54	#[document_type_parameters(
55		"The lifetime of the function and its captured data.",
56		"The brand of the thread-safe cloneable function wrapper.",
57		"The input and output type of the function."
58	)]
59	impl<'a, FnBrand: SendCloneableFn, A> SendEndofunction<'a, FnBrand, A> {
60		/// Creates a new `SendEndofunction`.
61		///
62		/// This function wraps a thread-safe function `a -> a` in a `SendEndofunction` struct.
63		///
64		/// ### Type Signature
65		///
66		#[document_signature]
67		///
68		/// ### Parameters
69		///
70		#[document_parameters("The function to wrap.")]
71		///
72		/// ### Returns
73		///
74		/// A new `SendEndofunction`.
75		///
76		/// ### Examples
77		///
78		/// ```
79		/// use fp_library::{brands::*, functions::*, types::*};
80		///
81		/// let f = SendEndofunction::<ArcFnBrand, _>::new(send_cloneable_fn_new::<ArcFnBrand, _, _>(|x: i32| x * 2));
82		/// assert_eq!(f.0(5), 10);
83		/// ```
84		pub fn new(f: <FnBrand as SendCloneableFn>::SendOf<'a, A, A>) -> Self {
85			Self(f)
86		}
87	}
88
89	/// ### Type Parameters
90	///
91	#[document_type_parameters(
92		"The lifetime of the function and its captured data.",
93		"The brand of the thread-safe cloneable function wrapper.",
94		"The input and output type of the function."
95	)]
96	#[document_parameters("The function to clone.")]
97	impl<'a, FnBrand: SendCloneableFn, A> Clone for SendEndofunction<'a, FnBrand, A> {
98		/// ### Type Signature
99		///
100		#[document_signature]
101		fn clone(&self) -> Self {
102			Self::new(self.0.clone())
103		}
104	}
105
106	/// ### Type Parameters
107	///
108	#[document_type_parameters(
109		"The lifetime of the function and its captured data.",
110		"The brand of the thread-safe cloneable function wrapper.",
111		"The input and output type of the function."
112	)]
113	#[document_parameters("The function to format.")]
114	impl<'a, FnBrand: SendCloneableFn, A> Debug for SendEndofunction<'a, FnBrand, A>
115	where
116		<FnBrand as SendCloneableFn>::SendOf<'a, A, A>: Debug,
117	{
118		/// ### Type Signature
119		///
120		#[document_signature]
121		///
122		/// ### Parameters
123		///
124		#[document_parameters("The formatter to use.")]
125		fn fmt(
126			&self,
127			fmt: &mut Formatter<'_>,
128		) -> fmt::Result {
129			fmt.debug_tuple("SendEndofunction").field(&self.0).finish()
130		}
131	}
132
133	/// ### Type Parameters
134	///
135	#[document_type_parameters(
136		"The lifetime of the function and its captured data.",
137		"The brand of the thread-safe cloneable function wrapper.",
138		"The input and output type of the function."
139	)]
140	impl<'a, FnBrand: SendCloneableFn, A> Eq for SendEndofunction<'a, FnBrand, A> where
141		<FnBrand as SendCloneableFn>::SendOf<'a, A, A>: Eq
142	{
143	}
144
145	/// ### Type Parameters
146	///
147	#[document_type_parameters(
148		"The lifetime of the function and its captured data.",
149		"The brand of the thread-safe cloneable function wrapper.",
150		"The input and output type of the function."
151	)]
152	#[document_parameters("The function to hash.")]
153	impl<'a, FnBrand: SendCloneableFn, A> Hash for SendEndofunction<'a, FnBrand, A>
154	where
155		<FnBrand as SendCloneableFn>::SendOf<'a, A, A>: Hash,
156	{
157		/// ### Type Signature
158		///
159		#[document_signature]
160		///
161		/// ### Type Parameters
162		///
163		#[document_type_parameters("The type of the hasher.")]
164		///
165		/// ### Parameters
166		///
167		#[document_parameters("The hasher state to update.")]
168		fn hash<H: std::hash::Hasher>(
169			&self,
170			state: &mut H,
171		) {
172			self.0.hash(state);
173		}
174	}
175
176	/// ### Type Parameters
177	///
178	#[document_type_parameters(
179		"The lifetime of the function and its captured data.",
180		"The brand of the thread-safe cloneable function wrapper.",
181		"The input and output type of the function."
182	)]
183	#[document_parameters("The function to compare.")]
184	impl<'a, FnBrand: SendCloneableFn, A> Ord for SendEndofunction<'a, FnBrand, A>
185	where
186		<FnBrand as SendCloneableFn>::SendOf<'a, A, A>: Ord,
187	{
188		/// ### Type Signature
189		///
190		#[document_signature]
191		///
192		/// ### Parameters
193		///
194		#[document_parameters("The other function to compare to.")]
195		fn cmp(
196			&self,
197			other: &Self,
198		) -> std::cmp::Ordering {
199			self.0.cmp(&other.0)
200		}
201	}
202
203	/// ### Type Parameters
204	///
205	#[document_type_parameters(
206		"The lifetime of the function and its captured data.",
207		"The brand of the thread-safe cloneable function wrapper.",
208		"The input and output type of the function."
209	)]
210	#[document_parameters("The function to compare.")]
211	impl<'a, FnBrand: SendCloneableFn, A> PartialEq for SendEndofunction<'a, FnBrand, A>
212	where
213		<FnBrand as SendCloneableFn>::SendOf<'a, A, A>: PartialEq,
214	{
215		/// ### Type Signature
216		///
217		#[document_signature]
218		///
219		/// ### Parameters
220		///
221		#[document_parameters("The other function to compare to.")]
222		fn eq(
223			&self,
224			other: &Self,
225		) -> bool {
226			self.0 == other.0
227		}
228	}
229
230	/// ### Type Parameters
231	///
232	#[document_type_parameters(
233		"The lifetime of the function and its captured data.",
234		"The brand of the thread-safe cloneable function wrapper.",
235		"The input and output type of the function."
236	)]
237	#[document_parameters("The function to compare.")]
238	impl<'a, FnBrand: SendCloneableFn, A> PartialOrd for SendEndofunction<'a, FnBrand, A>
239	where
240		<FnBrand as SendCloneableFn>::SendOf<'a, A, A>: PartialOrd,
241	{
242		/// ### Type Signature
243		///
244		#[document_signature]
245		///
246		/// ### Parameters
247		///
248		#[document_parameters("The other function to compare to.")]
249		fn partial_cmp(
250			&self,
251			other: &Self,
252		) -> Option<std::cmp::Ordering> {
253			self.0.partial_cmp(&other.0)
254		}
255	}
256
257	/// ### Type Parameters
258	///
259	#[document_type_parameters(
260		"The lifetime of the function and its captured data.",
261		"The brand of the thread-safe cloneable function wrapper.",
262		"The input and output type of the function."
263	)]
264	impl<'a, FnBrand: 'a + SendCloneableFn, A: 'a + Send + Sync> Semigroup
265		for SendEndofunction<'a, FnBrand, A>
266	{
267		/// The result of combining the two values using the semigroup operation.
268		///
269		/// This method combines two endofunctions into a single endofunction.
270		/// Note that `SendEndofunction` composition is reversed relative to standard function composition:
271		/// `append(f, g)` results in `f . g` (read as "f after g"), meaning `g` is applied first, then `f`.
272		///
273		/// ### Type Signature
274		///
275		#[document_signature]
276		///
277		/// ### Parameters
278		///
279		#[document_parameters(
280			"The second function to apply (the outer function).",
281			"The first function to apply (the inner function)."
282		)]
283		///
284		/// ### Returns
285		///
286		/// The composed function `a . b`.
287		///
288		/// ### Examples
289		///
290		/// ```
291		/// use fp_library::{brands::*, functions::*, types::*};
292		///
293		/// let f = SendEndofunction::<ArcFnBrand, _>::new(send_cloneable_fn_new::<ArcFnBrand, _, _>(|x: i32| x * 2));
294		/// let g = SendEndofunction::<ArcFnBrand, _>::new(send_cloneable_fn_new::<ArcFnBrand, _, _>(|x: i32| x + 1));
295		///
296		/// // f(g(x)) = (x + 1) * 2
297		/// let h = append::<_>(f, g);
298		/// assert_eq!(h.0(5), 12);
299		/// ```
300		fn append(
301			a: Self,
302			b: Self,
303		) -> Self {
304			let f = a.0;
305			let g = b.0;
306			// Compose: f . g
307			Self::new(<FnBrand as SendCloneableFn>::send_cloneable_fn_new(move |x| f(g(x))))
308		}
309	}
310
311	/// ### Type Parameters
312	///
313	#[document_type_parameters(
314		"The lifetime of the function and its captured data.",
315		"The brand of the thread-safe cloneable function wrapper.",
316		"The input and output type of the function."
317	)]
318	impl<'a, FnBrand: 'a + SendCloneableFn, A: 'a + Send + Sync> Monoid
319		for SendEndofunction<'a, FnBrand, A>
320	{
321		/// The identity element.
322		///
323		/// This method returns the identity endofunction, which wraps the identity function.
324		///
325		/// ### Type Signature
326		///
327		#[document_signature]
328		///
329		/// ### Returns
330		///
331		/// The identity endofunction.
332		///
333		/// ### Examples
334		///
335		/// ```
336		/// use fp_library::{brands::*, functions::*, types::*};
337		///
338		/// let id = empty::<SendEndofunction<ArcFnBrand, i32>>();
339		/// assert_eq!(id.0(5), 5);
340		/// ```
341		fn empty() -> Self {
342			Self::new(<FnBrand as SendCloneableFn>::send_cloneable_fn_new(identity))
343		}
344	}
345}
346
347pub use inner::*;