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