fp_library/types/
send_endofunction.rs

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