Skip to main content

fp_library/dispatch/
bifunctor.rs

1//! Dispatch for [`Bifunctor::bimap`](crate::classes::Bifunctor::bimap) and
2//! [`RefBifunctor::ref_bimap`](crate::classes::RefBifunctor::ref_bimap).
3//!
4//! Provides the [`BimapDispatch`] trait and a unified [`explicit::bimap`] free
5//! function that routes to the appropriate trait method based on the closures'
6//! argument types.
7//!
8//! ### Examples
9//!
10//! ```
11//! use fp_library::{
12//! 	brands::*,
13//! 	functions::explicit::*,
14//! };
15//!
16//! // Owned: dispatches to Bifunctor::bimap
17//! let x = Result::<i32, i32>::Ok(5);
18//! let y = bimap::<ResultBrand, _, _, _, _, _, _>((|e| e + 1, |s| s * 2), x);
19//! assert_eq!(y, Ok(10));
20//!
21//! // By-ref: dispatches to RefBifunctor::ref_bimap
22//! let x = Result::<i32, i32>::Ok(5);
23//! let y = bimap::<ResultBrand, _, _, _, _, _, _>((|e: &i32| *e + 1, |s: &i32| *s * 2), &x);
24//! assert_eq!(y, Ok(10));
25//! ```
26
27#[fp_macros::document_module]
28pub(crate) mod inner {
29	use {
30		crate::{
31			classes::{
32				Bifunctor,
33				RefBifunctor,
34			},
35			dispatch::{
36				Ref,
37				Val,
38			},
39			kinds::*,
40		},
41		fp_macros::*,
42	};
43
44	/// Trait that routes a bimap operation to the appropriate type class method.
45	///
46	/// The `Marker` type parameter is inferred from the closures' argument types:
47	/// `(Fn(A) -> B, Fn(C) -> D)` resolves to [`Val`](crate::dispatch::Val),
48	/// `(Fn(&A) -> B, Fn(&C) -> D)` resolves to [`Ref`](crate::dispatch::Ref).
49	/// The `FA` type parameter is inferred from the container argument: owned
50	/// for Val dispatch, borrowed for Ref dispatch.
51	#[document_type_parameters(
52		"The lifetime of the values.",
53		"The brand of the bifunctor.",
54		"The type of the first value.",
55		"The type of the first result.",
56		"The type of the second value.",
57		"The type of the second result.",
58		"The container type (owned or borrowed), inferred from the argument.",
59		"Dispatch marker type, inferred automatically."
60	)]
61	#[document_parameters("The closure tuple implementing this dispatch.")]
62	pub trait BimapDispatch<
63		'a,
64		Brand: Kind_266801a817966495,
65		A: 'a,
66		B: 'a,
67		C: 'a,
68		D: 'a,
69		FA,
70		Marker,
71	> {
72		/// Perform the dispatched bimap operation.
73		#[document_signature]
74		#[document_parameters("The bifunctor value.")]
75		#[document_returns("The result of bimapping.")]
76		#[document_examples]
77		///
78		/// ```
79		/// use fp_library::{
80		/// 	brands::*,
81		/// 	functions::explicit::*,
82		/// };
83		/// let result =
84		/// 	bimap::<ResultBrand, _, _, _, _, _, _>((|e| e + 1, |s: i32| s * 2), Ok::<i32, i32>(5));
85		/// assert_eq!(result, Ok(10));
86		/// ```
87		fn dispatch(
88			self,
89			fa: FA,
90		) -> Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, B, D>);
91	}
92
93	/// Routes `(Fn(A) -> B, Fn(C) -> D)` closure tuples to [`Bifunctor::bimap`].
94	#[document_type_parameters(
95		"The lifetime.",
96		"The brand.",
97		"The first input type.",
98		"The first output type.",
99		"The second input type.",
100		"The second output type.",
101		"The first closure type.",
102		"The second closure type."
103	)]
104	#[document_parameters("The closure tuple.")]
105	impl<'a, Brand, A, B, C, D, F, G>
106		BimapDispatch<
107			'a,
108			Brand,
109			A,
110			B,
111			C,
112			D,
113			Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, C>),
114			Val,
115		> for (F, G)
116	where
117		Brand: Bifunctor,
118		A: 'a,
119		B: 'a,
120		C: 'a,
121		D: 'a,
122		F: Fn(A) -> B + 'a,
123		G: Fn(C) -> D + 'a,
124	{
125		#[document_signature]
126		#[document_parameters("The bifunctor value.")]
127		#[document_returns("The result of bimapping.")]
128		#[document_examples]
129		///
130		/// ```
131		/// use fp_library::{
132		/// 	brands::*,
133		/// 	functions::explicit::*,
134		/// };
135		/// let result =
136		/// 	bimap::<ResultBrand, _, _, _, _, _, _>((|e| e + 1, |s: i32| s * 2), Ok::<i32, i32>(5));
137		/// assert_eq!(result, Ok(10));
138		/// ```
139		fn dispatch(
140			self,
141			fa: Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, C>),
142		) -> Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, B, D>) {
143			Brand::bimap(self.0, self.1, fa)
144		}
145	}
146
147	/// Routes `(Fn(&A) -> B, Fn(&C) -> D)` closure tuples to [`RefBifunctor::ref_bimap`].
148	///
149	/// The container must be passed by reference (`&p`).
150	#[document_type_parameters(
151		"The lifetime.",
152		"The borrow lifetime.",
153		"The brand.",
154		"The first input type.",
155		"The first output type.",
156		"The second input type.",
157		"The second output type.",
158		"The first closure type.",
159		"The second closure type."
160	)]
161	#[document_parameters("The closure tuple.")]
162	impl<'a, 'b, Brand, A, B, C, D, F, G>
163		BimapDispatch<
164			'a,
165			Brand,
166			A,
167			B,
168			C,
169			D,
170			&'b Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, C>),
171			Ref,
172		> for (F, G)
173	where
174		Brand: RefBifunctor,
175		A: 'a,
176		B: 'a,
177		C: 'a,
178		D: 'a,
179		F: Fn(&A) -> B + 'a,
180		G: Fn(&C) -> D + 'a,
181	{
182		#[document_signature]
183		#[document_parameters("A reference to the bifunctor value.")]
184		#[document_returns("The result of bimapping.")]
185		#[document_examples]
186		///
187		/// ```
188		/// use fp_library::{
189		/// 	brands::*,
190		/// 	functions::explicit::*,
191		/// };
192		/// let x = Result::<i32, i32>::Ok(5);
193		/// let result = bimap::<ResultBrand, _, _, _, _, _, _>((|e: &i32| *e + 1, |s: &i32| *s * 2), &x);
194		/// assert_eq!(result, Ok(10));
195		/// ```
196		fn dispatch(
197			self,
198			fa: &'b Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, C>),
199		) -> Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, B, D>) {
200			Brand::ref_bimap(self.0, self.1, fa)
201		}
202	}
203
204	// -- Inference wrapper --
205
206	/// Maps two functions over the values in a bifunctor context, inferring the
207	/// brand from the container type.
208	///
209	/// This is the primary API for bimapping. The `Brand` type parameter is
210	/// inferred from the concrete type of `p` via the `InferableBrand` trait. Both
211	/// owned and borrowed containers are supported.
212	///
213	/// For types that need an explicit brand, use
214	/// [`explicit::bimap`](crate::functions::explicit::bimap) with a turbofish.
215	#[document_signature]
216	///
217	#[document_type_parameters(
218		"The lifetime of the values.",
219		"The container type (owned or borrowed). Brand is inferred from this.",
220		"The type of the first value.",
221		"The type of the first result.",
222		"The type of the second value.",
223		"The type of the second result.",
224		"The brand, inferred via InferableBrand from FA and the closure's input type."
225	)]
226	///
227	#[document_parameters(
228		"A tuple of (first function, second function).",
229		"The bifunctor value (owned for Val, borrowed for Ref)."
230	)]
231	///
232	#[document_returns(
233		"A new bifunctor instance containing the results of applying the functions."
234	)]
235	#[document_examples]
236	///
237	/// ```
238	/// use fp_library::functions::*;
239	///
240	/// // Brand inferred from Result<i32, i32>
241	/// let x = Result::<i32, i32>::Ok(5);
242	/// let y = bimap((|e| e + 1, |s| s * 2), x);
243	/// assert_eq!(y, Ok(10));
244	///
245	/// // Brand inferred from &Result<i32, i32> via blanket impl
246	/// let x = Result::<i32, i32>::Ok(5);
247	/// let y = bimap((|e: &i32| *e + 1, |s: &i32| *s * 2), &x);
248	/// assert_eq!(y, Ok(10));
249	/// ```
250	pub fn bimap<'a, FA, A: 'a, B: 'a, C: 'a, D: 'a, Brand>(
251		fg: impl BimapDispatch<
252			'a,
253			Brand,
254			A,
255			B,
256			C,
257			D,
258			FA,
259			<FA as InferableBrand_266801a817966495<'a, Brand, A, C>>::Marker,
260		>,
261		p: FA,
262	) -> Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, B, D>)
263	where
264		Brand: Kind_266801a817966495,
265		FA: InferableBrand_266801a817966495<'a, Brand, A, C>, {
266		fg.dispatch(p)
267	}
268
269	// -- Explicit dispatch free function --
270
271	/// Explicit dispatch functions requiring a Brand turbofish.
272	///
273	/// For most use cases, prefer the inference-enabled wrappers from
274	/// [`functions`](crate::functions).
275	pub mod explicit {
276		use super::*;
277
278		/// Maps two functions over the values in a bifunctor context.
279		///
280		/// Dispatches to either [`Bifunctor::bimap`] or [`RefBifunctor::ref_bimap`]
281		/// based on the closures' argument types.
282		///
283		/// The `Marker` and `FA` type parameters are inferred automatically by the
284		/// compiler from the closures' argument types and the container argument.
285		/// Callers write `bimap::<Brand, _, _, _, _, _, _>(...)` and never need to
286		/// specify `Marker` or `FA` explicitly.
287		#[document_signature]
288		///
289		#[document_type_parameters(
290			"The lifetime of the values.",
291			"The brand of the bifunctor.",
292			"The type of the first value.",
293			"The type of the first result.",
294			"The type of the second value.",
295			"The type of the second result.",
296			"The container type (owned or borrowed), inferred from the argument.",
297			"Dispatch marker type, inferred automatically."
298		)]
299		///
300		#[document_parameters(
301			"A tuple of (first function, second function).",
302			"The bifunctor value (owned for Val, borrowed for Ref)."
303		)]
304		///
305		#[document_returns(
306			"A new bifunctor instance containing the results of applying the functions."
307		)]
308		#[document_examples]
309		///
310		/// ```
311		/// use fp_library::{
312		/// 	brands::*,
313		/// 	functions::explicit::*,
314		/// };
315		///
316		/// // Owned
317		/// let x = Result::<i32, i32>::Ok(5);
318		/// let y = bimap::<ResultBrand, _, _, _, _, _, _>((|e| e + 1, |s| s * 2), x);
319		/// assert_eq!(y, Ok(10));
320		///
321		/// // By-ref
322		/// let x = Result::<i32, i32>::Ok(5);
323		/// let y = bimap::<ResultBrand, _, _, _, _, _, _>((|e: &i32| *e + 1, |s: &i32| *s * 2), &x);
324		/// assert_eq!(y, Ok(10));
325		/// ```
326		pub fn bimap<'a, Brand: Kind_266801a817966495, A: 'a, B: 'a, C: 'a, D: 'a, FA, Marker>(
327			fg: impl BimapDispatch<'a, Brand, A, B, C, D, FA, Marker>,
328			p: FA,
329		) -> Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, B, D>) {
330			fg.dispatch(p)
331		}
332	}
333}
334
335pub use inner::*;