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