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}