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_cloneable_fn::SendCloneableFn},
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 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	/// `forall fn_brand a. (a -> a) -> SendEndofunction fn_brand a`
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	/// * `f`: 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	/// `forall fn_brand a. Semigroup (SendEndofunction fn_brand a) => (SendEndofunction fn_brand a, SendEndofunction fn_brand a) -> SendEndofunction fn_brand a`
165	///
166	/// ### Parameters
167	///
168	/// * `a`: The second function to apply (the outer function).
169	/// * `b`: The first function to apply (the inner function).
170	///
171	/// ### Returns
172	///
173	/// The composed function `a . b`.
174	///
175	/// ### Examples
176	///
177	/// ```
178	/// use fp_library::{brands::*, functions::*, types::*};
179	///
180	/// let f = SendEndofunction::<ArcFnBrand, _>::new(send_cloneable_fn_new::<ArcFnBrand, _, _>(|x: i32| x * 2));
181	/// let g = SendEndofunction::<ArcFnBrand, _>::new(send_cloneable_fn_new::<ArcFnBrand, _, _>(|x: i32| x + 1));
182	///
183	/// // f(g(x)) = (x + 1) * 2
184	/// let h = append::<_>(f, g);
185	/// assert_eq!(h.0(5), 12);
186	/// ```
187	fn append(
188		a: Self,
189		b: Self,
190	) -> Self {
191		let f = a.0;
192		let g = b.0;
193		// Compose: f . g
194		Self::new(<FnBrand as SendCloneableFn>::send_cloneable_fn_new(move |x| f(g(x))))
195	}
196}
197
198impl<'a, FnBrand: 'a + SendCloneableFn, A: 'a + Send + Sync> Monoid
199	for SendEndofunction<'a, FnBrand, A>
200{
201	/// The identity element.
202	///
203	/// This method returns the identity endofunction, which wraps the identity function.
204	///
205	/// ### Type Signature
206	///
207	/// `forall fn_brand a. Monoid (SendEndofunction fn_brand a) => () -> SendEndofunction fn_brand a`
208	///
209	/// ### Returns
210	///
211	/// The identity endofunction.
212	///
213	/// ### Examples
214	///
215	/// ```
216	/// use fp_library::{brands::*, functions::*, types::*};
217	///
218	/// let id = empty::<SendEndofunction<ArcFnBrand, i32>>();
219	/// assert_eq!(id.0(5), 5);
220	/// ```
221	fn empty() -> Self {
222		Self::new(<FnBrand as SendCloneableFn>::send_cloneable_fn_new(identity))
223	}
224}