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}