fp_library/types/arc_fn.rs
1//! Atomically reference-counted function wrapper.
2//!
3//! This module defines the [`ArcFnBrand`] struct, which provides implementations for atomically reference-counted closures (`Arc<dyn Fn(A) -> B>`).
4//! It implements [`Function`], [`ClonableFn`], [`SendClonableFn`], [`Semigroupoid`], and [`Category`].
5
6use crate::{
7 Apply,
8 brands::ArcFnBrand,
9 classes::{
10 category::Category, clonable_fn::ClonableFn, function::Function,
11 semigroupoid::Semigroupoid, send_clonable_fn::SendClonableFn,
12 },
13 impl_kind,
14 kinds::*,
15};
16use std::sync::Arc;
17
18impl_kind! {
19 for ArcFnBrand {
20 type Of<'a, A, B> = Arc<dyn 'a + Fn(A) -> B>;
21 }
22}
23
24impl Function for ArcFnBrand {
25 type Of<'a, A, B> = Apply!(<Self as Kind!( type Of<'a, T, U>; )>::Of<'a, A, B>);
26
27 /// Creates a new function wrapper.
28 ///
29 /// This function wraps the provided closure `f` into an `Arc`-wrapped function.
30 ///
31 /// ### Type Signature
32 ///
33 /// `forall a b. Function ArcFnBrand => (a -> b) -> ArcFnBrand a b`
34 ///
35 /// ### Type Parameters
36 ///
37 /// * `A`: The input type of the function.
38 /// * `B`: The output type of the function.
39 ///
40 /// ### Parameters
41 ///
42 /// * `f`: The closure to wrap.
43 ///
44 /// ### Returns
45 ///
46 /// The wrapped function.
47 ///
48 /// ### Examples
49 ///
50 /// ```
51 /// use fp_library::brands::ArcFnBrand;
52 /// use fp_library::classes::function::Function;
53 ///
54 /// let f = <ArcFnBrand as Function>::new(|x: i32| x * 2);
55 /// assert_eq!(f(5), 10);
56 /// ```
57 fn new<'a, A, B>(f: impl 'a + Fn(A) -> B) -> <Self as Function>::Of<'a, A, B> {
58 Arc::new(f)
59 }
60}
61
62impl ClonableFn for ArcFnBrand {
63 type Of<'a, A, B> = Apply!(<Self as Kind!( type Of<'a, T, U>; )>::Of<'a, A, B>);
64
65 /// Creates a new clonable function wrapper.
66 ///
67 /// This function wraps the provided closure `f` into an `Arc`-wrapped clonable function.
68 ///
69 /// ### Type Signature
70 ///
71 /// `forall a b. ClonableFn ArcFnBrand => (a -> b) -> ArcFnBrand a b`
72 ///
73 /// ### Type Parameters
74 ///
75 /// * `A`: The input type of the function.
76 /// * `B`: The output type of the function.
77 ///
78 /// ### Parameters
79 ///
80 /// * `f`: The closure to wrap.
81 ///
82 /// ### Returns
83 ///
84 /// The wrapped clonable function.
85 ///
86 /// ### Examples
87 ///
88 /// ```
89 /// use fp_library::brands::ArcFnBrand;
90 /// use fp_library::classes::clonable_fn::ClonableFn;
91 ///
92 /// let f = <ArcFnBrand as ClonableFn>::new(|x: i32| x * 2);
93 /// assert_eq!(f(5), 10);
94 /// ```
95 fn new<'a, A, B>(f: impl 'a + Fn(A) -> B) -> <Self as ClonableFn>::Of<'a, A, B> {
96 Arc::new(f)
97 }
98}
99
100impl SendClonableFn for ArcFnBrand {
101 type SendOf<'a, A, B> = Arc<dyn 'a + Fn(A) -> B + Send + Sync>;
102
103 /// Creates a new thread-safe clonable function wrapper.
104 ///
105 /// This method wraps a closure into an `Arc`-wrapped thread-safe clonable function.
106 ///
107 /// ### Type Signature
108 ///
109 /// `forall a b. SendClonableFn ArcFnBrand => (a -> b) -> ArcFnBrand a b`
110 ///
111 /// ### Type Parameters
112 ///
113 /// * `A`: The input type of the function.
114 /// * `B`: The output type of the function.
115 ///
116 /// ### Parameters
117 ///
118 /// * `f`: The closure to wrap. Must be `Send + Sync`.
119 ///
120 /// ### Returns
121 ///
122 /// The wrapped thread-safe clonable function.
123 ///
124 /// ### Examples
125 ///
126 /// ```
127 /// use fp_library::brands::ArcFnBrand;
128 /// use fp_library::classes::send_clonable_fn::SendClonableFn;
129 /// use std::thread;
130 ///
131 /// let f = <ArcFnBrand as SendClonableFn>::new_send(|x: i32| x * 2);
132 ///
133 /// // Can be sent to another thread
134 /// let handle = thread::spawn(move || {
135 /// assert_eq!(f(5), 10);
136 /// });
137 /// handle.join().unwrap();
138 /// ```
139 fn new_send<'a, A, B>(
140 f: impl 'a + Fn(A) -> B + Send + Sync
141 ) -> <Self as SendClonableFn>::SendOf<'a, A, B> {
142 Arc::new(f)
143 }
144}
145
146impl Semigroupoid for ArcFnBrand {
147 /// Takes morphisms `f` and `g` and returns the morphism `f . g` (`f` composed with `g`).
148 ///
149 /// This method composes two `Arc`-wrapped functions `f` and `g` to produce a new function that represents the application of `g` followed by `f`.
150 ///
151 /// ### Type Signature
152 ///
153 /// `forall b c d. Semigroupoid ArcFnBrand => (ArcFnBrand c d, ArcFnBrand b c) -> ArcFnBrand b d`
154 ///
155 /// ### Type Parameters
156 ///
157 /// * `B`: The source type of the first morphism.
158 /// * `C`: The target type of the first morphism and the source type of the second morphism.
159 /// * `D`: The target type of the second morphism.
160 ///
161 /// ### Parameters
162 ///
163 /// * `f`: The second morphism to apply (from C to D).
164 /// * `g`: The first morphism to apply (from B to C).
165 ///
166 /// ### Returns
167 ///
168 /// The composed morphism (from B to D).
169 ///
170 /// ### Examples
171 ///
172 /// ```
173 /// use fp_library::brands::ArcFnBrand;
174 /// use fp_library::classes::semigroupoid::Semigroupoid;
175 /// use fp_library::classes::clonable_fn::ClonableFn;
176 ///
177 /// let f = <ArcFnBrand as ClonableFn>::new(|x: i32| x * 2);
178 /// let g = <ArcFnBrand as ClonableFn>::new(|x: i32| x + 1);
179 /// let h = ArcFnBrand::compose(f, g);
180 /// assert_eq!(h(5), 12); // (5 + 1) * 2
181 /// ```
182 fn compose<'a, B: 'a, C: 'a, D: 'a>(
183 f: Apply!(<Self as Kind!( type Of<'a, T, U>; )>::Of<'a, C, D>),
184 g: Apply!(<Self as Kind!( type Of<'a, T, U>; )>::Of<'a, B, C>),
185 ) -> Apply!(<Self as Kind!( type Of<'a, T, U>; )>::Of<'a, B, D>) {
186 <Self as ClonableFn>::new(move |b| f(g(b)))
187 }
188}
189
190impl Category for ArcFnBrand {
191 /// Returns the identity morphism.
192 ///
193 /// The identity morphism is a function that maps every object to itself, wrapped in an `Arc`.
194 ///
195 /// ### Type Signature
196 ///
197 /// `forall a. Category ArcFnBrand => () -> ArcFnBrand a a`
198 ///
199 /// ### Type Parameters
200 ///
201 /// * `A`: The type of the object.
202 ///
203 /// ### Returns
204 ///
205 /// The identity morphism.
206 ///
207 /// ### Examples
208 ///
209 /// ```
210 /// use fp_library::brands::ArcFnBrand;
211 /// use fp_library::classes::category::Category;
212 ///
213 /// let id = ArcFnBrand::identity::<i32>();
214 /// assert_eq!(id(5), 5);
215 /// ```
216 fn identity<'a, A>() -> Apply!(<Self as Kind!( type Of<'a, T, U>; )>::Of<'a, A, A>) {
217 Arc::new(|a| a)
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224 use crate::classes::{
225 category::Category, clonable_fn::ClonableFn, semigroupoid::Semigroupoid,
226 send_clonable_fn::SendClonableFn,
227 };
228 use quickcheck_macros::quickcheck;
229 use std::thread;
230
231 // SendClonableFn Tests
232
233 /// Tests that `new_send` creates a callable function.
234 #[test]
235 fn new_send_callable() {
236 let f = <ArcFnBrand as SendClonableFn>::new_send(|x: i32| x * 2);
237 assert_eq!(f(5), 10);
238 }
239
240 /// Tests that the function can be cloned.
241 #[test]
242 fn send_clonable_clone() {
243 let f = <ArcFnBrand as SendClonableFn>::new_send(|x: i32| x * 2);
244 let g = f.clone();
245 assert_eq!(g(5), 10);
246 }
247
248 /// Tests that `SendOf` is `Send` (can be sent to another thread).
249 #[test]
250 fn send_of_is_send() {
251 let f = <ArcFnBrand as SendClonableFn>::new_send(|x: i32| x * 2);
252 let handle = thread::spawn(move || f(5));
253 assert_eq!(handle.join().unwrap(), 10);
254 }
255
256 /// Tests that `SendOf` is `Sync` (can be shared across threads).
257 #[test]
258 fn send_of_is_sync() {
259 let f = <ArcFnBrand as SendClonableFn>::new_send(|x: i32| x * 2);
260 let f_clone = f.clone();
261 let handle = thread::spawn(move || f_clone(5));
262 assert_eq!(f(5), 10);
263 assert_eq!(handle.join().unwrap(), 10);
264 }
265
266 // Semigroupoid Laws
267
268 /// Tests the associativity law for Semigroupoid.
269 #[quickcheck]
270 fn semigroupoid_associativity(x: i32) -> bool {
271 let f = <ArcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_add(1));
272 let g = <ArcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_mul(2));
273 let h = <ArcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_sub(3));
274
275 let lhs = ArcFnBrand::compose(f.clone(), ArcFnBrand::compose(g.clone(), h.clone()));
276 let rhs = ArcFnBrand::compose(ArcFnBrand::compose(f, g), h);
277
278 lhs(x) == rhs(x)
279 }
280
281 // Category Laws
282
283 /// Tests the left identity law for Category.
284 #[quickcheck]
285 fn category_left_identity(x: i32) -> bool {
286 let f = <ArcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_add(1));
287 let id = ArcFnBrand::identity::<i32>();
288
289 let lhs = ArcFnBrand::compose(id, f.clone());
290 let rhs = f;
291
292 lhs(x) == rhs(x)
293 }
294
295 /// Tests the right identity law for Category.
296 #[quickcheck]
297 fn category_right_identity(x: i32) -> bool {
298 let f = <ArcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_add(1));
299 let id = ArcFnBrand::identity::<i32>();
300
301 let lhs = ArcFnBrand::compose(f.clone(), id);
302 let rhs = f;
303
304 lhs(x) == rhs(x)
305 }
306}