Skip to main content

fp_library/dispatch/
bitraversable.rs

1//! Dispatch for [`Bitraversable::bi_traverse`](crate::classes::Bitraversable::bi_traverse) and
2//! [`RefBitraversable::ref_bi_traverse`](crate::classes::RefBitraversable::ref_bi_traverse).
3//!
4//! Provides the [`BiTraverseDispatch`] trait and a unified
5//! [`explicit::bi_traverse`] free function that routes to the appropriate trait
6//! method based on the closures' argument types.
7//!
8//! ### Examples
9//!
10//! ```
11//! use fp_library::{
12//! 	brands::*,
13//! 	functions::explicit::*,
14//! };
15//!
16//! // Owned: dispatches to Bitraversable::bi_traverse
17//! let x: Result<i32, i32> = Ok(5);
18//! let y = bi_traverse::<RcFnBrand, ResultBrand, _, _, _, _, OptionBrand, _, _>(
19//! 	(|e: i32| Some(e + 1), |s: i32| Some(s * 2)),
20//! 	x,
21//! );
22//! assert_eq!(y, Some(Ok(10)));
23//!
24//! // By-ref: dispatches to RefBitraversable::ref_bi_traverse
25//! let x: Result<i32, i32> = Ok(5);
26//! let y = bi_traverse::<RcFnBrand, ResultBrand, _, _, _, _, OptionBrand, _, _>(
27//! 	(|e: &i32| Some(e + 1), |s: &i32| Some(s * 2)),
28//! 	&x,
29//! );
30//! assert_eq!(y, Some(Ok(10)));
31//! ```
32
33#[fp_macros::document_module]
34pub(crate) mod inner {
35	use {
36		crate::{
37			classes::{
38				Applicative,
39				Bitraversable,
40				LiftFn,
41				RefBitraversable,
42			},
43			dispatch::{
44				Ref,
45				Val,
46			},
47			kinds::*,
48		},
49		fp_macros::*,
50	};
51
52	/// Trait that routes a bi_traverse operation to the appropriate type class method.
53	///
54	/// The `Marker` type parameter is an implementation detail resolved by
55	/// the compiler from the closures' argument types; callers never specify
56	/// it directly. The `FA` type parameter is inferred from the container
57	/// argument: owned for Val dispatch, borrowed for Ref dispatch.
58	#[document_type_parameters(
59		"The lifetime of the values.",
60		"The brand of the cloneable function to use.",
61		"The brand of the bitraversable structure.",
62		"The type of the first-position elements.",
63		"The type of the second-position elements.",
64		"The output type for first-position elements.",
65		"The output type for second-position elements.",
66		"The applicative functor brand for the computation.",
67		"The container type (owned or borrowed), inferred from the argument.",
68		"Dispatch marker type, inferred automatically."
69	)]
70	#[document_parameters("The closure tuple implementing this dispatch.")]
71	pub trait BiTraverseDispatch<
72		'a,
73		FnBrand,
74		Brand: Kind_266801a817966495,
75		A: 'a,
76		B: 'a,
77		C: 'a,
78		D: 'a,
79		F: Kind_cdc7cd43dac7585f,
80		FA,
81		Marker,
82	> {
83		/// Perform the dispatched bi_traverse operation.
84		#[document_signature]
85		///
86		#[document_parameters("The structure to traverse.")]
87		///
88		#[document_returns("The combined result in the applicative context.")]
89		#[document_examples]
90		///
91		/// ```
92		/// use fp_library::{
93		/// 	brands::*,
94		/// 	functions::explicit::*,
95		/// };
96		///
97		/// let x: Result<i32, i32> = Ok(5);
98		/// let result = bi_traverse::<RcFnBrand, ResultBrand, _, _, _, _, OptionBrand, _, _>(
99		/// 	(|e: i32| Some(e + 1), |s: i32| Some(s * 2)),
100		/// 	x,
101		/// );
102		/// assert_eq!(result, Some(Ok(10)));
103		/// ```
104		fn dispatch(
105			self,
106			fa: FA,
107		) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, C, D>)>);
108	}
109
110	// -- Val: (Fn(A) -> F<C>, Fn(B) -> F<D>) -> Bitraversable::bi_traverse --
111
112	/// Routes `(Fn(A) -> F::Of<C>, Fn(B) -> F::Of<D>)` closure tuples to [`Bitraversable::bi_traverse`].
113	///
114	/// The `FnBrand` parameter is unused by the Val path but is accepted for
115	/// uniformity with the Ref path.
116	#[document_type_parameters(
117		"The lifetime of the values.",
118		"The cloneable function brand (unused by Val path).",
119		"The brand of the bitraversable structure.",
120		"The first input type.",
121		"The second input type.",
122		"The first output type.",
123		"The second output type.",
124		"The applicative functor brand.",
125		"The first closure type.",
126		"The second closure type."
127	)]
128	#[document_parameters("The closure tuple.")]
129	impl<'a, FnBrand, Brand, A, B, C, D, F, Func1, Func2>
130		BiTraverseDispatch<
131			'a,
132			FnBrand,
133			Brand,
134			A,
135			B,
136			C,
137			D,
138			F,
139			Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
140			Val,
141		> for (Func1, Func2)
142	where
143		Brand: Bitraversable,
144		A: 'a + Clone,
145		B: 'a + Clone,
146		C: 'a + Clone,
147		D: 'a + Clone,
148		F: Applicative,
149		Func1: Fn(A) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>) + 'a,
150		Func2: Fn(B) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, D>) + 'a,
151	{
152		#[document_signature]
153		///
154		#[document_parameters("The structure to traverse.")]
155		///
156		#[document_returns("The combined result in the applicative context.")]
157		#[document_examples]
158		///
159		/// ```
160		/// use fp_library::{
161		/// 	brands::*,
162		/// 	functions::explicit::*,
163		/// };
164		///
165		/// let x: Result<i32, i32> = Ok(5);
166		/// let result = bi_traverse::<RcFnBrand, ResultBrand, _, _, _, _, OptionBrand, _, _>(
167		/// 	(|e: i32| Some(e + 1), |s: i32| Some(s * 2)),
168		/// 	x,
169		/// );
170		/// assert_eq!(result, Some(Ok(10)));
171		/// ```
172		fn dispatch(
173			self,
174			fa: Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
175		) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, C, D>)>)
176		{
177			Brand::bi_traverse::<A, B, C, D, F>(self.0, self.1, fa)
178		}
179	}
180
181	// -- Ref: (Fn(&A) -> F<C>, Fn(&B) -> F<D>) -> RefBitraversable::ref_bi_traverse --
182
183	/// Routes `(Fn(&A) -> F::Of<C>, Fn(&B) -> F::Of<D>)` closure tuples to [`RefBitraversable::ref_bi_traverse`].
184	///
185	/// The `FnBrand` parameter is passed through to the underlying
186	/// [`ref_bi_traverse`](RefBitraversable::ref_bi_traverse) call.
187	///
188	/// The container must be passed by reference (`&p`).
189	#[document_type_parameters(
190		"The lifetime of the values.",
191		"The borrow lifetime.",
192		"The cloneable function brand.",
193		"The brand of the bitraversable structure.",
194		"The first input type.",
195		"The second input type.",
196		"The first output type.",
197		"The second output type.",
198		"The applicative functor brand.",
199		"The first closure type.",
200		"The second closure type."
201	)]
202	#[document_parameters("The closure tuple.")]
203	impl<'a, 'b, FnBrand, Brand, A, B, C, D, F, Func1, Func2>
204		BiTraverseDispatch<
205			'a,
206			FnBrand,
207			Brand,
208			A,
209			B,
210			C,
211			D,
212			F,
213			&'b Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
214			Ref,
215		> for (Func1, Func2)
216	where
217		Brand: RefBitraversable,
218		FnBrand: LiftFn + 'a,
219		A: 'a + Clone,
220		B: 'a + Clone,
221		C: 'a + Clone,
222		D: 'a + Clone,
223		F: Applicative,
224		Func1: Fn(&A) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>) + 'a,
225		Func2: Fn(&B) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, D>) + 'a,
226		Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, C, D>): Clone,
227		Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>): Clone,
228		Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, D>): Clone,
229	{
230		#[document_signature]
231		///
232		#[document_parameters("A reference to the structure to traverse.")]
233		///
234		#[document_returns("The combined result in the applicative context.")]
235		#[document_examples]
236		///
237		/// ```
238		/// use fp_library::{
239		/// 	brands::*,
240		/// 	functions::explicit::*,
241		/// };
242		///
243		/// let x: Result<i32, i32> = Ok(5);
244		/// let result = bi_traverse::<RcFnBrand, ResultBrand, _, _, _, _, OptionBrand, _, _>(
245		/// 	(|e: &i32| Some(e + 1), |s: &i32| Some(s * 2)),
246		/// 	&x,
247		/// );
248		/// assert_eq!(result, Some(Ok(10)));
249		/// ```
250		fn dispatch(
251			self,
252			fa: &'b Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
253		) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, C, D>)>)
254		{
255			Brand::ref_bi_traverse::<FnBrand, A, B, C, D, F>(self.0, self.1, fa)
256		}
257	}
258
259	// -- Inference wrapper --
260
261	/// Traverses a bifoldable structure with an applicative effect, inferring
262	/// the brand from the container type.
263	///
264	/// The `Brand` type parameter is inferred from the concrete type of `fa` via
265	/// [`InferableBrand`](crate::kinds::InferableBrand_266801a817966495). `FnBrand`
266	/// and `F` (the applicative brand) must still be specified explicitly.
267	///
268	/// For types that need an explicit brand, use
269	/// [`explicit::bi_traverse`](crate::functions::explicit::bi_traverse).
270	#[document_signature]
271	///
272	#[document_type_parameters(
273		"The lifetime of the values.",
274		"The cloneable function brand.",
275		"The container type (owned or borrowed). Brand is inferred from this.",
276		"The type of the first element.",
277		"The type of the second element.",
278		"The type of the first result.",
279		"The type of the second result.",
280		"The applicative effect brand.",
281		"Dispatch marker type, inferred automatically."
282	)]
283	///
284	#[document_parameters(
285		"A tuple of (first traversal function, second traversal function).",
286		"The bitraversable value (owned for Val, borrowed for Ref)."
287	)]
288	///
289	#[document_returns("The applicative effect containing the traversed bifunctor.")]
290	#[document_examples]
291	///
292	/// ```
293	/// use fp_library::{
294	/// 	brands::*,
295	/// 	functions::*,
296	/// };
297	///
298	/// let x: Result<i32, i32> = Ok(5);
299	/// let y = bi_traverse::<RcFnBrand, _, _, _, _, _, OptionBrand, _>(
300	/// 	(|e: i32| Some(e + 1), |s: i32| Some(s * 2)),
301	/// 	x,
302	/// );
303	/// assert_eq!(y, Some(Ok(10)));
304	/// ```
305	pub fn bi_traverse<
306		'a,
307		FnBrand,
308		FA,
309		A: 'a,
310		B: 'a,
311		C: 'a,
312		D: 'a,
313		F: Kind_cdc7cd43dac7585f,
314		Marker,
315	>(
316		fg: impl BiTraverseDispatch<
317			'a,
318			FnBrand,
319			<FA as InferableBrand_266801a817966495>::Brand,
320			A,
321			B,
322			C,
323			D,
324			F,
325			FA,
326			Marker,
327		>,
328		fa: FA,
329	) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Apply!(<<FA as InferableBrand!(type Of<'a, A: 'a, B: 'a>: 'a;)>::Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, C, D>)>)
330	where
331		FA: InferableBrand_266801a817966495, {
332		fg.dispatch(fa)
333	}
334
335	// -- Explicit dispatch free function --
336
337	/// Explicit dispatch functions requiring a Brand turbofish.
338	///
339	/// For most use cases, prefer the inference-enabled wrappers from
340	/// [`functions`](crate::functions).
341	pub mod explicit {
342		use super::*;
343
344		/// Traverses a bitraversable structure, mapping each element to a computation and combining the results.
345		///
346		/// Dispatches to either [`Bitraversable::bi_traverse`] or
347		/// [`RefBitraversable::ref_bi_traverse`] based on the closures' argument types:
348		///
349		/// - If the closures take owned values and the container is owned,
350		///   dispatches to [`Bitraversable::bi_traverse`]. The `FnBrand` parameter
351		///   is unused but must be specified for uniformity.
352		/// - If the closures take references and the container is borrowed,
353		///   dispatches to [`RefBitraversable::ref_bi_traverse`]. The `FnBrand`
354		///   parameter is passed through as the function brand.
355		///
356		/// The `Marker` and `FA` type parameters are inferred automatically by
357		/// the compiler from the closures' argument types and the container
358		/// argument.
359		///
360		/// The dispatch is resolved at compile time with no runtime cost.
361		#[document_signature]
362		///
363		#[document_type_parameters(
364			"The lifetime of the values.",
365			"The brand of the cloneable function to use.",
366			"The brand of the bitraversable structure.",
367			"The type of the first-position elements.",
368			"The type of the second-position elements.",
369			"The output type for first-position elements.",
370			"The output type for second-position elements.",
371			"The applicative functor brand.",
372			"The container type (owned or borrowed), inferred from the argument.",
373			"Dispatch marker type, inferred automatically."
374		)]
375		///
376		#[document_parameters(
377			"A tuple of (first function, second function), each returning a value in an applicative context.",
378			"The bitraversable structure (owned for Val, borrowed for Ref)."
379		)]
380		///
381		#[document_returns("The structure wrapped in the applicative context.")]
382		///
383		#[document_examples]
384		///
385		/// ```
386		/// use fp_library::{
387		/// 	brands::*,
388		/// 	functions::explicit::*,
389		/// };
390		///
391		/// // Owned: dispatches to Bitraversable::bi_traverse
392		/// let x: Result<i32, i32> = Ok(5);
393		/// let y = bi_traverse::<RcFnBrand, ResultBrand, _, _, _, _, OptionBrand, _, _>(
394		/// 	(|e: i32| Some(e + 1), |s: i32| Some(s * 2)),
395		/// 	x,
396		/// );
397		/// assert_eq!(y, Some(Ok(10)));
398		///
399		/// // By-ref: dispatches to RefBitraversable::ref_bi_traverse
400		/// let x: Result<i32, i32> = Ok(5);
401		/// let y = bi_traverse::<RcFnBrand, ResultBrand, _, _, _, _, OptionBrand, _, _>(
402		/// 	(|e: &i32| Some(e + 1), |s: &i32| Some(s * 2)),
403		/// 	&x,
404		/// );
405		/// assert_eq!(y, Some(Ok(10)));
406		/// ```
407		pub fn bi_traverse<
408			'a,
409			FnBrand,
410			Brand: Kind_266801a817966495,
411			A: 'a,
412			B: 'a,
413			C: 'a,
414			D: 'a,
415			F: Kind_cdc7cd43dac7585f,
416			FA,
417			Marker,
418		>(
419			fg: impl BiTraverseDispatch<'a, FnBrand, Brand, A, B, C, D, F, FA, Marker>,
420			fa: FA,
421		) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, C, D>)>)
422		{
423			fg.dispatch(fa)
424		}
425	}
426}
427
428pub use inner::*;