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