Skip to main content

fp_library/dispatch/
contravariant.rs

1//! Dispatch for [`Contravariant::contramap`](crate::classes::Contravariant::contramap).
2//!
3//! Provides the [`ContravariantDispatch`] trait and a unified
4//! [`explicit::contramap`] free function. Unlike other dispatch modules, there
5//! is no Ref variant because `contramap`'s closure produces elements
6//! (`Fn(B) -> A`), not consumes them. The `&A` convention does not apply.
7//!
8//! ### Examples
9//!
10//! ```
11//! use fp_library::{
12//! 	brands::*,
13//! 	functions::explicit::*,
14//! };
15//!
16//! let f = std::rc::Rc::new(|x: i32| x + 1) as std::rc::Rc<dyn Fn(i32) -> i32>;
17//! let g =
18//! 	contramap::<ProfunctorSecondAppliedBrand<RcFnBrand, i32>, _, _, _, _>(|x: i32| x * 2, f);
19//! assert_eq!(g(5), 11); // (5 * 2) + 1
20//! ```
21
22#[fp_macros::document_module]
23pub(crate) mod inner {
24	use {
25		crate::{
26			classes::Contravariant,
27			dispatch::Val,
28			kinds::*,
29		},
30		fp_macros::*,
31	};
32
33	/// Trait that routes a contramap operation to [`Contravariant::contramap`].
34	///
35	/// Only a Val dispatch exists. There is no Ref variant because
36	/// `contramap`'s closure produces elements (`Fn(B) -> A`), not consumes
37	/// them, so the `&A` convention does not apply.
38	#[document_type_parameters(
39		"The lifetime of the values.",
40		"The brand of the contravariant functor.",
41		"The type of the value(s) inside the contravariant functor.",
42		"The type that the new contravariant functor accepts.",
43		"The container type, inferred from the argument.",
44		"Dispatch marker type, inferred automatically."
45	)]
46	#[document_parameters("The closure implementing this dispatch.")]
47	pub trait ContravariantDispatch<'a, Brand: Kind_cdc7cd43dac7585f, A: 'a, B: 'a, FA, Marker> {
48		/// Perform the dispatched contramap operation.
49		#[document_signature]
50		///
51		#[document_parameters("The contravariant functor instance.")]
52		///
53		#[document_returns("A new contravariant functor that accepts values of type `B`.")]
54		#[document_examples]
55		///
56		/// ```
57		/// use fp_library::{
58		/// 	brands::*,
59		/// 	functions::explicit::*,
60		/// };
61		///
62		/// let f = std::rc::Rc::new(|x: i32| x + 1) as std::rc::Rc<dyn Fn(i32) -> i32>;
63		/// let g =
64		/// 	contramap::<ProfunctorSecondAppliedBrand<RcFnBrand, i32>, _, _, _, _>(|x: i32| x * 2, f);
65		/// assert_eq!(g(5), 11);
66		/// ```
67		fn dispatch(
68			self,
69			fa: FA,
70		) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>);
71	}
72
73	// -- Val: Fn(B) -> A -> Contravariant::contramap --
74
75	/// Routes `Fn(B) -> A` closures to [`Contravariant::contramap`].
76	#[document_type_parameters(
77		"The lifetime of the values.",
78		"The brand of the contravariant functor.",
79		"The type of the value(s) inside the contravariant functor.",
80		"The type that the new contravariant functor accepts.",
81		"The closure type."
82	)]
83	#[document_parameters("The closure that maps the new input type to the original.")]
84	impl<'a, Brand, A, B, F>
85		ContravariantDispatch<
86			'a,
87			Brand,
88			A,
89			B,
90			Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
91			Val,
92		> for F
93	where
94		Brand: Contravariant,
95		A: 'a,
96		B: 'a,
97		F: Fn(B) -> A + 'a,
98	{
99		#[document_signature]
100		///
101		#[document_parameters("The contravariant functor instance.")]
102		///
103		#[document_returns("A new contravariant functor that accepts values of type `B`.")]
104		#[document_examples]
105		///
106		/// ```
107		/// use fp_library::{
108		/// 	brands::*,
109		/// 	functions::explicit::*,
110		/// };
111		///
112		/// let f = std::rc::Rc::new(|x: i32| x + 1) as std::rc::Rc<dyn Fn(i32) -> i32>;
113		/// let g =
114		/// 	contramap::<ProfunctorSecondAppliedBrand<RcFnBrand, i32>, _, _, _, _>(|x: i32| x * 2, f);
115		/// assert_eq!(g(5), 11);
116		/// ```
117		fn dispatch(
118			self,
119			fa: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
120		) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
121			Brand::contramap(self, fa)
122		}
123	}
124
125	// -- Inference wrapper --
126
127	/// Contravariantly maps a function over a value, inferring the brand from the container type.
128	///
129	/// The `Brand` type parameter is inferred from the concrete type of `fa`
130	/// via the `InferableBrand` trait. Only owned containers are supported; there is no
131	/// `RefContravariant` trait because the Ref pattern is about closures
132	/// receiving element references (`&A`), but `contramap`'s closure
133	/// produces elements (`Fn(B) -> A`), not consumes them.
134	///
135	/// For types with multiple brands, use
136	/// [`explicit::contramap`](crate::functions::explicit::contramap) with a turbofish.
137	#[document_signature]
138	///
139	#[document_type_parameters(
140		"The lifetime of the values.",
141		"The container type. Brand is inferred from this.",
142		"The type of the value(s) inside the contravariant functor.",
143		"The type that the new contravariant functor accepts.",
144		"The brand, inferred via InferableBrand from FA and the element type."
145	)]
146	///
147	#[document_parameters(
148		"The function mapping the new input type to the original input type.",
149		"The contravariant functor instance."
150	)]
151	///
152	#[document_returns("A new contravariant functor that accepts values of type `B`.")]
153	#[document_examples]
154	///
155	/// This example uses [`explicit::contramap`](crate::functions::explicit::contramap)
156	/// because the primary contravariant types are profunctor-based (e.g.,
157	/// `Rc<dyn Fn(A) -> B>`), which have multiple possible brands depending
158	/// on which type parameter varies. Brand inference cannot resolve this
159	/// ambiguity, so a turbofish is required.
160	///
161	/// ```
162	/// use fp_library::{
163	/// 	brands::*,
164	/// 	functions::explicit::*,
165	/// };
166	///
167	/// let f = std::rc::Rc::new(|x: i32| x + 1) as std::rc::Rc<dyn Fn(i32) -> i32>;
168	/// let g =
169	/// 	contramap::<ProfunctorSecondAppliedBrand<RcFnBrand, i32>, _, _, _, _>(|x: i32| x * 2, f);
170	/// assert_eq!(g(5), 11);
171	/// ```
172	pub fn contramap<'a, FA, A: 'a, B: 'a, Brand>(
173		f: impl ContravariantDispatch<
174			'a,
175			Brand,
176			A,
177			B,
178			FA,
179			<FA as InferableBrand_cdc7cd43dac7585f<'a, Brand, A>>::Marker,
180		>,
181		fa: FA,
182	) -> <Brand as Kind_cdc7cd43dac7585f>::Of<'a, B>
183	where
184		Brand: Kind_cdc7cd43dac7585f,
185		FA: InferableBrand_cdc7cd43dac7585f<'a, Brand, A>, {
186		f.dispatch(fa)
187	}
188
189	// -- Explicit dispatch free function --
190
191	/// Explicit dispatch functions requiring a Brand turbofish.
192	///
193	/// For most use cases, prefer the inference-enabled wrappers from
194	/// [`functions`](crate::functions).
195	pub mod explicit {
196		use super::*;
197
198		/// Contravariantly maps a function over a contravariant functor.
199		///
200		/// Dispatches to [`Contravariant::contramap`].
201		///
202		/// The `Marker` and `FA` type parameters are inferred automatically by the
203		/// compiler. Callers write `contramap::<Brand, _, _, _, _>(...)` and never
204		/// need to specify `Marker` or `FA` explicitly.
205		#[document_signature]
206		///
207		#[document_type_parameters(
208			"The lifetime of the values.",
209			"The brand of the contravariant functor.",
210			"The type of the value(s) inside the contravariant functor.",
211			"The type that the new contravariant functor accepts.",
212			"The container type, inferred from the argument.",
213			"Dispatch marker type, inferred automatically."
214		)]
215		///
216		#[document_parameters(
217			"The function mapping the new input type to the original input type.",
218			"The contravariant functor instance."
219		)]
220		///
221		#[document_returns("A new contravariant functor that accepts values of type `B`.")]
222		#[document_examples]
223		///
224		/// ```
225		/// use fp_library::{
226		/// 	brands::*,
227		/// 	functions::explicit::*,
228		/// };
229		///
230		/// let f = std::rc::Rc::new(|x: i32| x + 1) as std::rc::Rc<dyn Fn(i32) -> i32>;
231		/// let g =
232		/// 	contramap::<ProfunctorSecondAppliedBrand<RcFnBrand, i32>, _, _, _, _>(|x: i32| x * 2, f);
233		/// assert_eq!(g(5), 11);
234		/// ```
235		pub fn contramap<'a, Brand: Kind_cdc7cd43dac7585f, A: 'a, B: 'a, FA, Marker>(
236			f: impl ContravariantDispatch<'a, Brand, A, B, FA, Marker>,
237			fa: FA,
238		) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
239			f.dispatch(fa)
240		}
241	}
242}
243
244pub use inner::*;