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}