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