Skip to main content

fp_library/dispatch/
traversable.rs

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