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}