fp_library/types/
arc_fn.rs

1//! Implementations for [atomically reference-counted][std::sync::Arc]
2//! [closures][Fn] (`Arc<dyn Fn(A) -> B>`).
3
4use crate::{
5	Apply,
6	brands::ArcFnBrand,
7	classes::{
8		category::Category, clonable_fn::ClonableFn, function::Function, semigroupoid::Semigroupoid,
9	},
10	impl_kind,
11	kinds::*,
12};
13use std::sync::Arc;
14
15impl_kind! {
16	for ArcFnBrand {
17		type Of<'a, A, B> = Arc<dyn 'a + Fn(A) -> B>;
18	}
19}
20
21impl Function for ArcFnBrand {
22	type Of<'a, A, B> = Apply!(brand: Self, signature: ('a, A, B));
23
24	/// Creates a new `Arc`-wrapped function.
25	///
26	/// # Type Signature
27	///
28	/// `forall a b. Function ArcFnBrand => (a -> b) -> ArcFnBrand a b`
29	///
30	/// # Parameters
31	///
32	/// * `f`: The function to wrap.
33	///
34	/// # Returns
35	///
36	/// An `Arc`-wrapped function.
37	///
38	/// # Examples
39	///
40	/// ```
41	/// use fp_library::brands::ArcFnBrand;
42	/// use fp_library::classes::function::Function;
43	///
44	/// let f = <ArcFnBrand as Function>::new(|x: i32| x * 2);
45	/// assert_eq!(f(5), 10);
46	/// ```
47	fn new<'a, A, B>(
48		f: impl 'a + Fn(A) -> B
49	) -> Apply!(brand: Self, kind: Function, lifetimes: ('a), types: (A, B)) {
50		Arc::new(f)
51	}
52}
53
54impl ClonableFn for ArcFnBrand {
55	type Of<'a, A, B> = Apply!(brand: Self, signature: ('a, A, B));
56
57	/// Creates a new clonable function wrapper.
58	///
59	/// # Type Signature
60	///
61	/// `forall a b. ClonableFn ArcFnBrand => (a -> b) -> ArcFnBrand a b`
62	///
63	/// # Parameters
64	///
65	/// * `f`: The function to wrap.
66	///
67	/// # Returns
68	///
69	/// An `Arc`-wrapped clonable function.
70	///
71	/// # Examples
72	///
73	/// ```
74	/// use fp_library::brands::ArcFnBrand;
75	/// use fp_library::classes::clonable_fn::ClonableFn;
76	///
77	/// let f = <ArcFnBrand as ClonableFn>::new(|x: i32| x * 2);
78	/// assert_eq!(f(5), 10);
79	/// ```
80	fn new<'a, A, B>(
81		f: impl 'a + Fn(A) -> B
82	) -> Apply!(brand: Self, kind: ClonableFn, lifetimes: ('a), types: (A, B)) {
83		Arc::new(f)
84	}
85}
86
87impl Semigroupoid for ArcFnBrand {
88	/// Composes two `Arc`-wrapped functions.
89	///
90	/// # Type Signature
91	///
92	/// `forall b c d. Semigroupoid ArcFnBrand => (ArcFnBrand c d, ArcFnBrand b c) -> ArcFnBrand b d`
93	///
94	/// # Parameters
95	///
96	/// * `f`: The second function to apply.
97	/// * `g`: The first function to apply.
98	///
99	/// # Returns
100	///
101	/// The composed function `f . g`.
102	///
103	/// # Examples
104	///
105	/// ```
106	/// use fp_library::brands::ArcFnBrand;
107	/// use fp_library::classes::semigroupoid::Semigroupoid;
108	/// use fp_library::classes::clonable_fn::ClonableFn;
109	///
110	/// let f = <ArcFnBrand as ClonableFn>::new(|x: i32| x * 2);
111	/// let g = <ArcFnBrand as ClonableFn>::new(|x: i32| x + 1);
112	/// let h = ArcFnBrand::compose(f, g);
113	/// assert_eq!(h(5), 12); // (5 + 1) * 2
114	/// ```
115	fn compose<'a, B: 'a, C: 'a, D: 'a>(
116		f: Apply!(brand: Self, signature: ('a, C, D)),
117		g: Apply!(brand: Self, signature: ('a, B, C)),
118	) -> Apply!(brand: Self, signature: ('a, B, D)) {
119		<Self as ClonableFn>::new(move |b| f(g(b)))
120	}
121}
122
123impl Category for ArcFnBrand {
124	/// Returns the identity function wrapped in an `Arc`.
125	///
126	/// # Type Signature
127	///
128	/// `forall a. Category ArcFnBrand => () -> ArcFnBrand a a`
129	///
130	/// # Returns
131	///
132	/// The identity function.
133	///
134	/// # Examples
135	///
136	/// ```
137	/// use fp_library::brands::ArcFnBrand;
138	/// use fp_library::classes::category::Category;
139	///
140	/// let id = ArcFnBrand::identity::<i32>();
141	/// assert_eq!(id(5), 5);
142	/// ```
143	fn identity<'a, A>() -> Apply!(brand: Self, signature: ('a, A, A)) {
144		Arc::new(|a| a)
145	}
146}
147
148#[cfg(test)]
149mod tests {
150	use super::*;
151	use crate::classes::{category::Category, clonable_fn::ClonableFn, semigroupoid::Semigroupoid};
152	use quickcheck_macros::quickcheck;
153
154	// Semigroupoid Laws
155
156	/// Tests the associativity law for Semigroupoid.
157	#[quickcheck]
158	fn semigroupoid_associativity(x: i32) -> bool {
159		let f = <ArcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_add(1));
160		let g = <ArcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_mul(2));
161		let h = <ArcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_sub(3));
162
163		let lhs = ArcFnBrand::compose(f.clone(), ArcFnBrand::compose(g.clone(), h.clone()));
164		let rhs = ArcFnBrand::compose(ArcFnBrand::compose(f, g), h);
165
166		lhs(x) == rhs(x)
167	}
168
169	// Category Laws
170
171	/// Tests the left identity law for Category.
172	#[quickcheck]
173	fn category_left_identity(x: i32) -> bool {
174		let f = <ArcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_add(1));
175		let id = ArcFnBrand::identity::<i32>();
176
177		let lhs = ArcFnBrand::compose(id, f.clone());
178		let rhs = f;
179
180		lhs(x) == rhs(x)
181	}
182
183	/// Tests the right identity law for Category.
184	#[quickcheck]
185	fn category_right_identity(x: i32) -> bool {
186		let f = <ArcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_add(1));
187		let id = ArcFnBrand::identity::<i32>();
188
189		let lhs = ArcFnBrand::compose(f.clone(), id);
190		let rhs = f;
191
192		lhs(x) == rhs(x)
193	}
194}