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}