Skip to main content

fp_library/types/
send_endofunction.rs

1//! Thread-safe wrapper for endofunctions with [`Semigroup`] and [`Monoid`] instances.
2//!
3//! The `Send + Sync` counterpart to [`Endofunction`](crate::types::Endofunction), wrapping functions that can be safely shared across threads.
4
5use crate::{
6	classes::{Monoid, Semigroup, SendCloneableFn},
7	functions::identity,
8};
9use fp_macros::{doc_params, hm_signature};
10use std::{
11	fmt::{self, Debug, Formatter},
12	hash::Hash,
13};
14
15/// A thread-safe wrapper for endofunctions (functions from a set to the same set) that enables monoidal operations.
16///
17/// `SendEndofunction a` represents a function `a -> a` that is `Send + Sync`.
18///
19/// It exists to provide a monoid instance where:
20///
21/// * The binary operation [append][Semigroup::append] is [function composition][crate::functions::compose].
22/// * The identity element [empty][Monoid::empty] is the [identity function][crate::functions::identity].
23///
24/// The wrapped function can be accessed directly via the [`.0` field][SendEndofunction#structfield.0].
25///
26/// ### Type Parameters
27///
28/// * `FnBrand`: The brand of the thread-safe cloneable function wrapper.
29/// * `A`: The input and output type of the function.
30///
31/// ### Fields
32///
33/// * `0`: The wrapped thread-safe function.
34///
35/// ### Examples
36///
37/// ```
38/// use fp_library::{brands::*, functions::*, types::*};
39///
40/// let f = SendEndofunction::<ArcFnBrand, _>::new(send_cloneable_fn_new::<ArcFnBrand, _, _>(|x: i32| x * 2));
41/// assert_eq!(f.0(5), 10);
42/// ```
43pub struct SendEndofunction<'a, FnBrand: SendCloneableFn, A>(
44	pub <FnBrand as SendCloneableFn>::SendOf<'a, A, A>,
45);
46
47impl<'a, FnBrand: SendCloneableFn, A> SendEndofunction<'a, FnBrand, A> {
48	/// Creates a new `SendEndofunction`.
49	///
50	/// This function wraps a thread-safe function `a -> a` in a `SendEndofunction` struct.
51	///
52	/// ### Type Signature
53	///
54	#[hm_signature]
55	///
56	/// ### Type Parameters
57	///
58	/// * `FnBrand`: The brand of the function (e.g., `ArcFnBrand`).
59	/// * `A`: The input and output type of the function.
60	///
61	/// ### Parameters
62	///
63	#[doc_params("The function to wrap.")]
64	///
65	/// ### Returns
66	///
67	/// A new `SendEndofunction`.
68	///
69	/// ### Examples
70	///
71	/// ```
72	/// use fp_library::{brands::*, functions::*, types::*};
73	///
74	/// let f = SendEndofunction::<ArcFnBrand, _>::new(send_cloneable_fn_new::<ArcFnBrand, _, _>(|x: i32| x * 2));
75	/// assert_eq!(f.0(5), 10);
76	/// ```
77	pub fn new(f: <FnBrand as SendCloneableFn>::SendOf<'a, A, A>) -> Self {
78		Self(f)
79	}
80}
81
82impl<'a, FnBrand: SendCloneableFn, A> Clone for SendEndofunction<'a, FnBrand, A> {
83	fn clone(&self) -> Self {
84		Self::new(self.0.clone())
85	}
86}
87
88impl<'a, FnBrand: SendCloneableFn, A> Debug for SendEndofunction<'a, FnBrand, A>
89where
90	<FnBrand as SendCloneableFn>::SendOf<'a, A, A>: Debug,
91{
92	fn fmt(
93		&self,
94		fmt: &mut Formatter<'_>,
95	) -> fmt::Result {
96		fmt.debug_tuple("SendEndofunction").field(&self.0).finish()
97	}
98}
99
100impl<'a, FnBrand: SendCloneableFn, A> Eq for SendEndofunction<'a, FnBrand, A> where
101	<FnBrand as SendCloneableFn>::SendOf<'a, A, A>: Eq
102{
103}
104
105impl<'a, FnBrand: SendCloneableFn, A> Hash for SendEndofunction<'a, FnBrand, A>
106where
107	<FnBrand as SendCloneableFn>::SendOf<'a, A, A>: Hash,
108{
109	fn hash<H: std::hash::Hasher>(
110		&self,
111		state: &mut H,
112	) {
113		self.0.hash(state);
114	}
115}
116
117impl<'a, FnBrand: SendCloneableFn, A> Ord for SendEndofunction<'a, FnBrand, A>
118where
119	<FnBrand as SendCloneableFn>::SendOf<'a, A, A>: Ord,
120{
121	fn cmp(
122		&self,
123		other: &Self,
124	) -> std::cmp::Ordering {
125		self.0.cmp(&other.0)
126	}
127}
128
129impl<'a, FnBrand: SendCloneableFn, A> PartialEq for SendEndofunction<'a, FnBrand, A>
130where
131	<FnBrand as SendCloneableFn>::SendOf<'a, A, A>: PartialEq,
132{
133	fn eq(
134		&self,
135		other: &Self,
136	) -> bool {
137		self.0 == other.0
138	}
139}
140
141impl<'a, FnBrand: SendCloneableFn, A> PartialOrd for SendEndofunction<'a, FnBrand, A>
142where
143	<FnBrand as SendCloneableFn>::SendOf<'a, A, A>: PartialOrd,
144{
145	fn partial_cmp(
146		&self,
147		other: &Self,
148	) -> Option<std::cmp::Ordering> {
149		self.0.partial_cmp(&other.0)
150	}
151}
152
153impl<'a, FnBrand: 'a + SendCloneableFn, A: 'a + Send + Sync> Semigroup
154	for SendEndofunction<'a, FnBrand, A>
155{
156	/// The result of combining the two values using the semigroup operation.
157	///
158	/// This method combines two endofunctions into a single endofunction.
159	/// Note that `SendEndofunction` composition is reversed relative to standard function composition:
160	/// `append(f, g)` results in `f . g` (read as "f after g"), meaning `g` is applied first, then `f`.
161	///
162	/// ### Type Signature
163	///
164	#[hm_signature(Semigroup)]
165	///
166	/// ### Parameters
167	///
168	#[doc_params(
169		"The second function to apply (the outer function).",
170		"The first function to apply (the inner function)."
171	)]
172	///
173	/// ### Returns
174	///
175	/// The composed function `a . b`.
176	///
177	/// ### Examples
178	///
179	/// ```
180	/// use fp_library::{brands::*, functions::*, types::*};
181	///
182	/// let f = SendEndofunction::<ArcFnBrand, _>::new(send_cloneable_fn_new::<ArcFnBrand, _, _>(|x: i32| x * 2));
183	/// let g = SendEndofunction::<ArcFnBrand, _>::new(send_cloneable_fn_new::<ArcFnBrand, _, _>(|x: i32| x + 1));
184	///
185	/// // f(g(x)) = (x + 1) * 2
186	/// let h = append::<_>(f, g);
187	/// assert_eq!(h.0(5), 12);
188	/// ```
189	fn append(
190		a: Self,
191		b: Self,
192	) -> Self {
193		let f = a.0;
194		let g = b.0;
195		// Compose: f . g
196		Self::new(<FnBrand as SendCloneableFn>::send_cloneable_fn_new(move |x| f(g(x))))
197	}
198}
199
200impl<'a, FnBrand: 'a + SendCloneableFn, A: 'a + Send + Sync> Monoid
201	for SendEndofunction<'a, FnBrand, A>
202{
203	/// The identity element.
204	///
205	/// This method returns the identity endofunction, which wraps the identity function.
206	///
207	/// ### Type Signature
208	///
209	#[hm_signature(Monoid)]
210	///
211	/// ### Returns
212	///
213	/// The identity endofunction.
214	///
215	/// ### Examples
216	///
217	/// ```
218	/// use fp_library::{brands::*, functions::*, types::*};
219	///
220	/// let id = empty::<SendEndofunction<ArcFnBrand, i32>>();
221	/// assert_eq!(id.0(5), 5);
222	/// ```
223	fn empty() -> Self {
224		Self::new(<FnBrand as SendCloneableFn>::send_cloneable_fn_new(identity))
225	}
226}