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