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::functions::*;
53 ///
54 /// let f = fn_new::<ArcFnBrand, _, _>(|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::functions::*;
91 ///
92 /// let f = clonable_fn_new::<ArcFnBrand, _, _>(|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::functions::*;
129 /// use std::thread;
130 ///
131 /// let f = send_clonable_fn_new::<ArcFnBrand, _, _>(|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 send_clonable_fn_new<'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 d c. 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 /// * `D`: The target type of the second morphism.
159 /// * `C`: The target type of the first morphism and the source 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::functions::*;
175 ///
176 /// let f = clonable_fn_new::<ArcFnBrand, _, _>(|x: i32| x * 2);
177 /// let g = clonable_fn_new::<ArcFnBrand, _, _>(|x: i32| x + 1);
178 /// let h = semigroupoid_compose::<ArcFnBrand, _, _, _>(f, g);
179 /// assert_eq!(h(5), 12); // (5 + 1) * 2
180 /// ```
181 fn compose<'a, B: 'a, D: 'a, C: 'a>(
182 f: Apply!(<Self as Kind!( type Of<'a, T, U>; )>::Of<'a, C, D>),
183 g: Apply!(<Self as Kind!( type Of<'a, T, U>; )>::Of<'a, B, C>),
184 ) -> Apply!(<Self as Kind!( type Of<'a, T, U>; )>::Of<'a, B, D>) {
185 <Self as ClonableFn>::new(move |b| f(g(b)))
186 }
187}
188
189impl Category for ArcFnBrand {
190 /// Returns the identity morphism.
191 ///
192 /// The identity morphism is a function that maps every object to itself, wrapped in an `Arc`.
193 ///
194 /// ### Type Signature
195 ///
196 /// `forall a. Category ArcFnBrand => () -> ArcFnBrand a a`
197 ///
198 /// ### Type Parameters
199 ///
200 /// * `A`: The type of the object.
201 ///
202 /// ### Returns
203 ///
204 /// The identity morphism.
205 ///
206 /// ### Examples
207 ///
208 /// ```
209 /// use fp_library::brands::ArcFnBrand;
210 /// use fp_library::functions::*;
211 ///
212 /// let id = category_identity::<ArcFnBrand, i32>();
213 /// assert_eq!(id(5), 5);
214 /// ```
215 fn identity<'a, A>() -> Apply!(<Self as Kind!( type Of<'a, T, U>; )>::Of<'a, A, A>) {
216 Arc::new(|a| a)
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223 use crate::classes::{
224 category::Category, clonable_fn::ClonableFn, semigroupoid::Semigroupoid,
225 send_clonable_fn::SendClonableFn,
226 };
227 use quickcheck_macros::quickcheck;
228 use std::thread;
229
230 // SendClonableFn Tests
231
232 /// Tests that `send_clonable_fn_new` creates a callable function.
233 #[test]
234 fn send_clonable_fn_new_callable() {
235 let f = <ArcFnBrand as SendClonableFn>::send_clonable_fn_new(|x: i32| x * 2);
236 assert_eq!(f(5), 10);
237 }
238
239 /// Tests that the function can be cloned.
240 #[test]
241 fn send_clonable_clone() {
242 let f = <ArcFnBrand as SendClonableFn>::send_clonable_fn_new(|x: i32| x * 2);
243 let g = f.clone();
244 assert_eq!(g(5), 10);
245 }
246
247 /// Tests that `SendOf` is `Send` (can be sent to another thread).
248 #[test]
249 fn send_of_is_send() {
250 let f = <ArcFnBrand as SendClonableFn>::send_clonable_fn_new(|x: i32| x * 2);
251 let handle = thread::spawn(move || f(5));
252 assert_eq!(handle.join().unwrap(), 10);
253 }
254
255 /// Tests that `SendOf` is `Sync` (can be shared across threads).
256 #[test]
257 fn send_of_is_sync() {
258 let f = <ArcFnBrand as SendClonableFn>::send_clonable_fn_new(|x: i32| x * 2);
259 let f_clone = f.clone();
260 let handle = thread::spawn(move || f_clone(5));
261 assert_eq!(f(5), 10);
262 assert_eq!(handle.join().unwrap(), 10);
263 }
264
265 // Semigroupoid Laws
266
267 /// Tests the associativity law for Semigroupoid.
268 #[quickcheck]
269 fn semigroupoid_associativity(x: i32) -> bool {
270 let f = <ArcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_add(1));
271 let g = <ArcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_mul(2));
272 let h = <ArcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_sub(3));
273
274 let lhs = <ArcFnBrand as Semigroupoid>::compose(
275 f.clone(),
276 <ArcFnBrand as Semigroupoid>::compose(g.clone(), h.clone()),
277 );
278 let rhs =
279 <ArcFnBrand as Semigroupoid>::compose(<ArcFnBrand as Semigroupoid>::compose(f, g), h);
280
281 lhs(x) == rhs(x)
282 }
283
284 // Category Laws
285
286 /// Tests the left identity law for Category.
287 #[quickcheck]
288 fn category_left_identity(x: i32) -> bool {
289 let f = <ArcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_add(1));
290 let id = <ArcFnBrand as Category>::identity::<i32>();
291
292 let lhs = <ArcFnBrand as Semigroupoid>::compose(id, f.clone());
293 let rhs = f;
294
295 lhs(x) == rhs(x)
296 }
297
298 /// Tests the right identity law for Category.
299 #[quickcheck]
300 fn category_right_identity(x: i32) -> bool {
301 let f = <ArcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_add(1));
302 let id = <ArcFnBrand as Category>::identity::<i32>();
303
304 let lhs = <ArcFnBrand as Semigroupoid>::compose(f.clone(), id);
305 let rhs = f;
306
307 lhs(x) == rhs(x)
308 }
309}