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}