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}