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}