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