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