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!(<Self as Kind!( type Of<'a, T, U>; )>::Of<'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::functions::*;
52	///
53	/// let f = fn_new::<RcFnBrand, _, _>(|x: i32| x * 2);
54	/// assert_eq!(f(5), 10);
55	/// ```
56	fn new<'a, A, B>(f: impl 'a + Fn(A) -> B) -> <Self as Function>::Of<'a, A, B> {
57		Rc::new(f)
58	}
59}
60
61impl ClonableFn for RcFnBrand {
62	type Of<'a, A, B> = Apply!(<Self as Kind!( type Of<'a, T, U>; )>::Of<'a, A, B>);
63
64	/// Creates a new clonable function wrapper.
65	///
66	/// This function wraps the provided closure `f` into an `Rc`-wrapped clonable function.
67	///
68	/// ### Type Signature
69	///
70	/// `forall a b. ClonableFn RcFnBrand => (a -> b) -> RcFnBrand a b`
71	///
72	/// ### Type Parameters
73	///
74	/// * `A`: The input type of the function.
75	/// * `B`: The output type of the function.
76	///
77	/// ### Parameters
78	///
79	/// * `f`: The closure to wrap.
80	///
81	/// ### Returns
82	///
83	/// The wrapped clonable function.
84	///
85	/// ### Examples
86	///
87	/// ```
88	/// use fp_library::brands::RcFnBrand;
89	/// use fp_library::functions::*;
90	///
91	/// let f = clonable_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2);
92	/// assert_eq!(f(5), 10);
93	/// ```
94	fn new<'a, A, B>(f: impl 'a + Fn(A) -> B) -> <Self as ClonableFn>::Of<'a, A, B> {
95		Rc::new(f)
96	}
97}
98
99impl Semigroupoid for RcFnBrand {
100	/// Takes morphisms `f` and `g` and returns the morphism `f . g` (`f` composed with `g`).
101	///
102	/// This method composes two `Rc`-wrapped functions `f` and `g` to produce a new function that represents the application of `g` followed by `f`.
103	///
104	/// ### Type Signature
105	///
106	/// `forall b d c. Semigroupoid RcFnBrand => (RcFnBrand c d, RcFnBrand b c) -> RcFnBrand b d`
107	///
108	/// ### Type Parameters
109	///
110	/// * `B`: The source type of the first morphism.
111	/// * `D`: The target type of the second morphism.
112	/// * `C`: The target type of the first morphism and the source type of the second morphism.
113	///
114	/// ### Parameters
115	///
116	/// * `f`: The second morphism to apply (from C to D).
117	/// * `g`: The first morphism to apply (from B to C).
118	///
119	/// ### Returns
120	///
121	/// The composed morphism (from B to D).
122	///
123	/// ### Examples
124	///
125	/// ```
126	/// use fp_library::brands::RcFnBrand;
127	/// use fp_library::functions::*;
128	///
129	/// let f = clonable_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2);
130	/// let g = clonable_fn_new::<RcFnBrand, _, _>(|x: i32| x + 1);
131	/// let h = semigroupoid_compose::<RcFnBrand, _, _, _>(f, g);
132	/// assert_eq!(h(5), 12); // (5 + 1) * 2
133	/// ```
134	fn compose<'a, B: 'a, D: 'a, C: 'a>(
135		f: Apply!(<Self as Kind!( type Of<'a, T, U>; )>::Of<'a, C, D>),
136		g: Apply!(<Self as Kind!( type Of<'a, T, U>; )>::Of<'a, B, C>),
137	) -> Apply!(<Self as Kind!( type Of<'a, T, U>; )>::Of<'a, B, D>) {
138		<Self as ClonableFn>::new(move |b| f(g(b)))
139	}
140}
141
142impl Category for RcFnBrand {
143	/// Returns the identity morphism.
144	///
145	/// The identity morphism is a function that maps every object to itself, wrapped in an `Rc`.
146	///
147	/// ### Type Signature
148	///
149	/// `forall a. Category RcFnBrand => () -> RcFnBrand a a`
150	///
151	/// ### Type Parameters
152	///
153	/// * `A`: The type of the object.
154	///
155	/// ### Returns
156	///
157	/// The identity morphism.
158	///
159	/// ### Examples
160	///
161	/// ```
162	/// use fp_library::brands::RcFnBrand;
163	/// use fp_library::functions::*;
164	///
165	/// let id = category_identity::<RcFnBrand, i32>();
166	/// assert_eq!(id(5), 5);
167	/// ```
168	fn identity<'a, A>() -> Apply!(<Self as Kind!( type Of<'a, T, U>; )>::Of<'a, A, A>) {
169		Rc::new(|a| a)
170	}
171}
172
173#[cfg(test)]
174mod tests {
175	use super::*;
176	use crate::classes::{category::Category, clonable_fn::ClonableFn, semigroupoid::Semigroupoid};
177	use quickcheck_macros::quickcheck;
178
179	// Semigroupoid Laws
180
181	/// Tests the associativity law for Semigroupoid.
182	#[quickcheck]
183	fn semigroupoid_associativity(x: i32) -> bool {
184		let f = <RcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_add(1));
185		let g = <RcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_mul(2));
186		let h = <RcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_sub(3));
187
188		let lhs = RcFnBrand::compose(f.clone(), RcFnBrand::compose(g.clone(), h.clone()));
189		let rhs = RcFnBrand::compose(RcFnBrand::compose(f, g), h);
190
191		lhs(x) == rhs(x)
192	}
193
194	// Category Laws
195
196	/// Tests the left identity law for Category.
197	#[quickcheck]
198	fn category_left_identity(x: i32) -> bool {
199		let f = <RcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_add(1));
200		let id = RcFnBrand::identity::<i32>();
201
202		let lhs = RcFnBrand::compose(id, f.clone());
203		let rhs = f;
204
205		lhs(x) == rhs(x)
206	}
207
208	/// Tests the right identity law for Category.
209	#[quickcheck]
210	fn category_right_identity(x: i32) -> bool {
211		let f = <RcFnBrand as ClonableFn>::new(|x: i32| x.wrapping_add(1));
212		let id = RcFnBrand::identity::<i32>();
213
214		let lhs = RcFnBrand::compose(f.clone(), id);
215		let rhs = f;
216
217		lhs(x) == rhs(x)
218	}
219}