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	/// the `InferableBrand` trait. `FnBrand` and `F` (the applicative brand) must still be
266	/// 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		"The brand, inferred via InferableBrand from FA and the closure's input type."
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		Brand,
315	>(
316		fg: impl BiTraverseDispatch<
317			'a,
318			FnBrand,
319			Brand,
320			A,
321			B,
322			C,
323			D,
324			F,
325			FA,
326			<FA as InferableBrand_266801a817966495<'a, Brand, A, B>>::Marker,
327		>,
328		fa: FA,
329	) -> 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>)>)
330	where
331		Brand: Kind_266801a817966495,
332		FA: InferableBrand_266801a817966495<'a, Brand, A, B>, {
333		fg.dispatch(fa)
334	}
335
336	// -- Explicit dispatch free function --
337
338	/// Explicit dispatch functions requiring a Brand turbofish.
339	///
340	/// For most use cases, prefer the inference-enabled wrappers from
341	/// [`functions`](crate::functions).
342	pub mod explicit {
343		use super::*;
344
345		/// Traverses a bitraversable structure, mapping each element to a computation and combining the results.
346		///
347		/// Dispatches to either [`Bitraversable::bi_traverse`] or
348		/// [`RefBitraversable::ref_bi_traverse`] based on the closures' argument types:
349		///
350		/// - If the closures take owned values and the container is owned,
351		///   dispatches to [`Bitraversable::bi_traverse`]. The `FnBrand` parameter
352		///   is unused but must be specified for uniformity.
353		/// - If the closures take references and the container is borrowed,
354		///   dispatches to [`RefBitraversable::ref_bi_traverse`]. The `FnBrand`
355		///   parameter is passed through as the function brand.
356		///
357		/// The `Marker` and `FA` type parameters are inferred automatically by
358		/// the compiler from the closures' argument types and the container
359		/// argument.
360		///
361		/// The dispatch is resolved at compile time with no runtime cost.
362		#[document_signature]
363		///
364		#[document_type_parameters(
365			"The lifetime of the values.",
366			"The brand of the cloneable function to use.",
367			"The brand of the bitraversable structure.",
368			"The type of the first-position elements.",
369			"The type of the second-position elements.",
370			"The output type for first-position elements.",
371			"The output type for second-position elements.",
372			"The applicative functor brand.",
373			"The container type (owned or borrowed), inferred from the argument.",
374			"Dispatch marker type, inferred automatically."
375		)]
376		///
377		#[document_parameters(
378			"A tuple of (first function, second function), each returning a value in an applicative context.",
379			"The bitraversable structure (owned for Val, borrowed for Ref)."
380		)]
381		///
382		#[document_returns("The structure wrapped in the applicative context.")]
383		///
384		#[document_examples]
385		///
386		/// ```
387		/// use fp_library::{
388		/// 	brands::*,
389		/// 	functions::explicit::*,
390		/// };
391		///
392		/// // Owned: dispatches to Bitraversable::bi_traverse
393		/// let x: Result<i32, i32> = Ok(5);
394		/// let y = bi_traverse::<RcFnBrand, ResultBrand, _, _, _, _, OptionBrand, _, _>(
395		/// 	(|e: i32| Some(e + 1), |s: i32| Some(s * 2)),
396		/// 	x,
397		/// );
398		/// assert_eq!(y, Some(Ok(10)));
399		///
400		/// // By-ref: dispatches to RefBitraversable::ref_bi_traverse
401		/// let x: Result<i32, i32> = Ok(5);
402		/// let y = bi_traverse::<RcFnBrand, ResultBrand, _, _, _, _, OptionBrand, _, _>(
403		/// 	(|e: &i32| Some(e + 1), |s: &i32| Some(s * 2)),
404		/// 	&x,
405		/// );
406		/// assert_eq!(y, Some(Ok(10)));
407		/// ```
408		pub fn bi_traverse<
409			'a,
410			FnBrand,
411			Brand: Kind_266801a817966495,
412			A: 'a,
413			B: 'a,
414			C: 'a,
415			D: 'a,
416			F: Kind_cdc7cd43dac7585f,
417			FA,
418			Marker,
419		>(
420			fg: impl BiTraverseDispatch<'a, FnBrand, Brand, A, B, C, D, F, FA, Marker>,
421			fa: FA,
422		) -> 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>)>)
423		{
424			fg.dispatch(fa)
425		}
426	}
427}
428
429pub use inner::*;