Skip to main content

fp_library/dispatch/
traversable_with_index.rs

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