fp_library/types/rc_fn.rs
1//! Reference-counted function wrapper.
2//!
3//! This module defines the [`RcFnBrand`] struct, which provides implementations for reference-counted closures (`Rc<dyn Fn(A) -> B>`).
4//! It implements [`Function`], [`ClonableFn`], [`Semigroupoid`], and [`Category`].
5
6use crate::{
7 Apply,
8 brands::RcFnBrand,
9 classes::{
10 category::Category, clonable_fn::ClonableFn, function::Function, semigroupoid::Semigroupoid,
11 },
12 impl_kind,
13 kinds::*,
14};
15use std::rc::Rc;
16
17impl_kind! {
18 for RcFnBrand {
19 type Of<'a, A, B> = Rc<dyn 'a + Fn(A) -> B>;
20 }
21}
22
23impl Function for RcFnBrand {
24 type Of<'a, A, B> = Apply!(brand: Self, signature: ('a, A, B));
25
26 /// Creates a new function wrapper.
27 ///
28 /// This function wraps the provided closure `f` into an `Rc`-wrapped function.
29 ///
30 /// ### Type Signature
31 ///
32 /// `forall a b. Function RcFnBrand => (a -> b) -> RcFnBrand a b`
33 ///
34 /// ### Type Parameters
35 ///
36 /// * `A`: The input type of the function.
37 /// * `B`: The output type of the function.
38 ///
39 /// ### Parameters
40 ///
41 /// * `f`: The closure to wrap.
42 ///
43 /// ### Returns
44 ///
45 /// The wrapped function.
46 ///
47 /// ### Examples
48 ///
49 /// ```
50 /// use fp_library::brands::RcFnBrand;
51 /// use fp_library::classes::function::Function;
52 ///
53 /// let f = <RcFnBrand as Function>::new(|x: i32| x * 2);
54 /// assert_eq!(f(5), 10);
55 /// ```
56 fn new<'a, A, B>(
57 f: impl 'a + Fn(A) -> B
58 ) -> Apply!(brand: Self, kind: Function, lifetimes: ('a), types: (A, B)) {
59 Rc::new(f)
60 }
61}
62
63impl ClonableFn for RcFnBrand {
64 type Of<'a, A, B> = Apply!(brand: Self, signature: ('a, A, B));
65
66 /// Creates a new clonable function wrapper.
67 ///
68 /// This function wraps the provided closure `f` into an `Rc`-wrapped clonable function.
69 ///
70 /// ### Type Signature
71 ///
72 /// `forall a b. ClonableFn RcFnBrand => (a -> b) -> RcFnBrand a b`
73 ///
74 /// ### Type Parameters
75 ///
76 /// * `A`: The input type of the function.
77 /// * `B`: The output type of the function.
78 ///
79 /// ### Parameters
80 ///
81 /// * `f`: The closure to wrap.
82 ///
83 /// ### Returns
84 ///
85 /// The wrapped clonable function.
86 ///
87 /// ### Examples
88 ///
89 /// ```
90 /// use fp_library::brands::RcFnBrand;
91 /// use fp_library::classes::clonable_fn::ClonableFn;
92 ///
93 /// let f = <RcFnBrand as ClonableFn>::new(|x: i32| x * 2);
94 /// assert_eq!(f(5), 10);
95 /// ```
96 fn new<'a, A, B>(
97 f: impl 'a + Fn(A) -> B
98 ) -> Apply!(brand: Self, kind: ClonableFn, lifetimes: ('a), types: (A, B)) {
99 Rc::new(f)
100 }
101}
102
103impl Semigroupoid for RcFnBrand {
104 /// Takes morphisms `f` and `g` and returns the morphism `f . g` (`f` composed with `g`).
105 ///
106 /// This method composes two `Rc`-wrapped functions `f` and `g` to produce a new function that represents the application of `g` followed by `f`.
107 ///
108 /// ### Type Signature
109 ///
110 /// `forall b c d. Semigroupoid RcFnBrand => (RcFnBrand c d, RcFnBrand b c) -> RcFnBrand b d`
111 ///
112 /// ### Type Parameters
113 ///
114 /// * `B`: The source type of the first morphism.
115 /// * `C`: The target type of the first morphism and the source type of the second morphism.
116 /// * `D`: The target type of the second morphism.
117 ///
118 /// ### Parameters
119 ///
120 /// * `f`: The second morphism to apply (from C to D).
121 /// * `g`: The first morphism to apply (from B to C).
122 ///
123 /// ### Returns
124 ///
125 /// The composed morphism (from B to D).
126 ///
127 /// ### Examples
128 ///
129 /// ```
130 /// use fp_library::brands::RcFnBrand;
131 /// use fp_library::classes::semigroupoid::Semigroupoid;
132 /// use fp_library::classes::clonable_fn::ClonableFn;
133 ///
134 /// let f = <RcFnBrand as ClonableFn>::new(|x: i32| x * 2);
135 /// let g = <RcFnBrand as ClonableFn>::new(|x: i32| x + 1);
136 /// let h = RcFnBrand::compose(f, g);
137 /// assert_eq!(h(5), 12); // (5 + 1) * 2
138 /// ```
139 fn compose<'a, B: 'a, C: 'a, D: 'a>(
140 f: Apply!(brand: Self, signature: ('a, C, D)),
141 g: Apply!(brand: Self, signature: ('a, B, C)),
142 ) -> Apply!(brand: Self, signature: ('a, B, D)) {
143 <Self as ClonableFn>::new(move |b| f(g(b)))
144 }
145}
146
147impl Category for RcFnBrand {
148 /// Returns the identity morphism.
149 ///
150 /// The identity morphism is a function that maps every object to itself, wrapped in an `Rc`.
151 ///
152 /// ### Type Signature
153 ///
154 /// `forall a. Category RcFnBrand => () -> RcFnBrand a a`
155 ///
156 /// ### Type Parameters
157 ///
158 /// * `A`: The type of the object.
159 ///
160 /// ### Returns
161 ///
162 /// The identity morphism.
163 ///
164 /// ### Examples
165 ///
166 /// ```
167 /// use fp_library::brands::RcFnBrand;
168 /// use fp_library::classes::category::Category;
169 ///
170 /// let id = RcFnBrand::identity::<i32>();
171 /// assert_eq!(id(5), 5);
172 /// ```
173 fn identity<'a, A>() -> Apply!(brand: Self, signature: ('a, A, A)) {
174 Rc::new(|a| a)
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181 use crate::classes::{category::Category, clonable_fn::ClonableFn, semigroupoid::Semigroupoid};
182 use quickcheck_macros::quickcheck;
183
184 // Semigroupoid Laws
185
186 /// Tests the associativity law for Semigroupoid.
187 #[quickcheck]
188 fn semigroupoid_associativity(x: i32) -> bool {
189 let f = <RcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_add(1));
190 let g = <RcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_mul(2));
191 let h = <RcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_sub(3));
192
193 let lhs = RcFnBrand::compose(f.clone(), RcFnBrand::compose(g.clone(), h.clone()));
194 let rhs = RcFnBrand::compose(RcFnBrand::compose(f, g), h);
195
196 lhs(x) == rhs(x)
197 }
198
199 // Category Laws
200
201 /// Tests the left identity law for Category.
202 #[quickcheck]
203 fn category_left_identity(x: i32) -> bool {
204 let f = <RcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_add(1));
205 let id = RcFnBrand::identity::<i32>();
206
207 let lhs = RcFnBrand::compose(id, f.clone());
208 let rhs = f;
209
210 lhs(x) == rhs(x)
211 }
212
213 /// Tests the right identity law for Category.
214 #[quickcheck]
215 fn category_right_identity(x: i32) -> bool {
216 let f = <RcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_add(1));
217 let id = RcFnBrand::identity::<i32>();
218
219 let lhs = RcFnBrand::compose(f.clone(), id);
220 let rhs = f;
221
222 lhs(x) == rhs(x)
223 }
224}