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}