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