Skip to main content

fp_library/classes/
bitraversable.rs

1//! Data structures with two type arguments that can be traversed in an applicative context.
2//!
3//! ### Examples
4//!
5//! ```
6//! use fp_library::{
7//! 	brands::*,
8//! 	functions::*,
9//! };
10//!
11//! let x: Result<i32, i32> = Ok(5);
12//! let y = bi_traverse::<ResultBrand, _, _, _, _, OptionBrand>(
13//! 	|e: i32| Some(e + 1),
14//! 	|s: i32| Some(s * 2),
15//! 	x,
16//! );
17//! assert_eq!(y, Some(Ok(10)));
18//! ```
19
20#[fp_macros::document_module]
21mod inner {
22	use {
23		crate::{
24			classes::*,
25			functions::*,
26			kinds::*,
27		},
28		fp_macros::*,
29	};
30
31	/// A type class for data structures with two type arguments that can be traversed.
32	///
33	/// A `Bitraversable` represents a container with two type parameters whose elements
34	/// can be traversed with effectful functions, accumulating results in an applicative
35	/// context. A traversal requires two functions, one for each type argument.
36	///
37	/// ### Minimal Implementation
38	///
39	/// A minimal implementation requires [`Bitraversable::bi_traverse`] to be defined directly.
40	/// [`Bitraversable::bi_sequence`] is derived from it via `identity`.
41	///
42	/// Note: defining both defaults creates a circular dependency and will not terminate.
43	///
44	/// ### Laws
45	///
46	/// `Bitraversable` instances must be consistent with `Bifunctor` and `Bifoldable` when
47	/// those are also defined, following the general principle that the structure of the
48	/// traversal mirrors the structure of the map and fold.
49	pub trait Bitraversable: Bifunctor + Bifoldable {
50		/// Traverses the bitraversable structure with two effectful functions.
51		///
52		/// This method applies `f` to values of the first type and `g` to values of the
53		/// second type, accumulating all effects in the applicative context `F`.
54		#[document_signature]
55		///
56		#[document_type_parameters(
57			"The lifetime of the values.",
58			"The type of the first-position elements.",
59			"The type of the second-position elements.",
60			"The output type for first-position elements.",
61			"The output type for second-position elements.",
62			"The applicative context."
63		)]
64		///
65		#[document_parameters(
66			"The function for first-position elements.",
67			"The function for second-position elements.",
68			"The bitraversable structure to traverse."
69		)]
70		///
71		#[document_returns("The transformed structure wrapped in the applicative context.")]
72		#[document_examples]
73		///
74		/// ```
75		/// use fp_library::{
76		/// 	brands::*,
77		/// 	functions::*,
78		/// };
79		///
80		/// let x: Result<i32, i32> = Err(3);
81		/// let y = bi_traverse::<ResultBrand, _, _, _, _, OptionBrand>(
82		/// 	|e: i32| Some(e + 1),
83		/// 	|s: i32| Some(s * 2),
84		/// 	x,
85		/// );
86		/// assert_eq!(y, Some(Err(4)));
87		/// ```
88		fn bi_traverse<
89			'a,
90			A: 'a + Clone,
91			B: 'a + Clone,
92			C: 'a + Clone,
93			D: 'a + Clone,
94			F: Applicative,
95		>(
96			f: impl Fn(A) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>) + 'a,
97			g: impl Fn(B) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, D>) + 'a,
98			p: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
99		) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, C, D>)>);
100
101		/// Sequences a bitraversable structure containing applicative values.
102		///
103		/// Collapses a structure of effectful values into a single effectful structure,
104		/// applying `identity` to both positions via [`Bitraversable::bi_traverse`].
105		#[document_signature]
106		///
107		#[document_type_parameters(
108			"The lifetime of the values.",
109			"The type of the first-position elements.",
110			"The type of the second-position elements.",
111			"The applicative context."
112		)]
113		///
114		#[document_parameters("The bitraversable structure containing applicative values.")]
115		///
116		#[document_returns("The applicative context wrapping the bitraversable structure.")]
117		#[document_examples]
118		///
119		/// ```
120		/// use fp_library::{
121		/// 	brands::*,
122		/// 	functions::*,
123		/// };
124		///
125		/// let x: Result<Option<i32>, Option<i32>> = Ok(Some(5));
126		/// let y = bi_sequence::<ResultBrand, _, _, OptionBrand>(x);
127		/// assert_eq!(y, Some(Ok(5)));
128		/// ```
129		fn bi_sequence<'a, A: 'a + Clone, B: 'a + Clone, F: Applicative>(
130			ta: Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>), Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>)>)
131		) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>)>)
132		where
133			Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone,
134			Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>): Clone, {
135			Self::bi_traverse::<
136				Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
137				Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
138				A,
139				B,
140				F,
141			>(identity, identity, ta)
142		}
143	}
144
145	/// Traverses the bitraversable structure with two effectful functions.
146	///
147	/// Free function version that dispatches to [the type class' associated function][`Bitraversable::bi_traverse`].
148	#[document_signature]
149	///
150	#[document_type_parameters(
151		"The lifetime of the values.",
152		"The brand of the bitraversable structure.",
153		"The type of the first-position elements.",
154		"The type of the second-position elements.",
155		"The output type for first-position elements.",
156		"The output type for second-position elements.",
157		"The applicative context."
158	)]
159	///
160	#[document_parameters(
161		"The function for first-position elements.",
162		"The function for second-position elements.",
163		"The bitraversable structure to traverse."
164	)]
165	///
166	#[document_returns("The transformed structure wrapped in the applicative context.")]
167	#[document_examples]
168	///
169	/// ```
170	/// use fp_library::{
171	/// 	brands::*,
172	/// 	functions::*,
173	/// };
174	///
175	/// let x: Result<i32, i32> = Ok(5);
176	/// let y = bi_traverse::<ResultBrand, _, _, _, _, OptionBrand>(
177	/// 	|e: i32| Some(e + 1),
178	/// 	|s: i32| Some(s * 2),
179	/// 	x,
180	/// );
181	/// assert_eq!(y, Some(Ok(10)));
182	/// ```
183	pub fn bi_traverse<
184		'a,
185		Brand: Bitraversable,
186		A: 'a + Clone,
187		B: 'a + Clone,
188		C: 'a + Clone,
189		D: 'a + Clone,
190		F: Applicative,
191	>(
192		f: impl Fn(A) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>) + 'a,
193		g: impl Fn(B) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, D>) + 'a,
194		p: Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
195	) -> 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>)>)
196	{
197		Brand::bi_traverse::<A, B, C, D, F>(f, g, p)
198	}
199
200	/// Sequences a bitraversable structure containing applicative values.
201	///
202	/// Free function version that dispatches to [the type class' associated function][`Bitraversable::bi_sequence`].
203	#[document_signature]
204	///
205	#[document_type_parameters(
206		"The lifetime of the values.",
207		"The brand of the bitraversable structure.",
208		"The type of the first-position elements.",
209		"The type of the second-position elements.",
210		"The applicative context."
211	)]
212	///
213	#[document_parameters("The bitraversable structure containing applicative values.")]
214	///
215	#[document_returns("The applicative context wrapping the bitraversable structure.")]
216	#[document_examples]
217	///
218	/// ```
219	/// use fp_library::{
220	/// 	brands::*,
221	/// 	functions::*,
222	/// };
223	///
224	/// let x: Result<Option<i32>, Option<i32>> = Ok(Some(5));
225	/// let y = bi_sequence::<ResultBrand, _, _, OptionBrand>(x);
226	/// assert_eq!(y, Some(Ok(5)));
227	/// ```
228	pub fn bi_sequence<'a, Brand: Bitraversable, A: 'a + Clone, B: 'a + Clone, F: Applicative>(
229		ta: Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>), Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>)>)
230	) -> 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, A, B>)>)
231	where
232		Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone,
233		Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>): Clone, {
234		Brand::bi_sequence::<A, B, F>(ta)
235	}
236
237	/// Traverses only the first-position elements, leaving second-position elements unchanged via `pure`.
238	///
239	/// Equivalent to `bi_traverse(f, pure, p)`.
240	#[document_signature]
241	///
242	#[document_type_parameters(
243		"The lifetime of the values.",
244		"The brand of the bitraversable structure.",
245		"The type of the first-position elements.",
246		"The type of the second-position elements (unchanged).",
247		"The output type for first-position elements.",
248		"The applicative context."
249	)]
250	///
251	#[document_parameters(
252		"The function for first-position elements.",
253		"The bitraversable structure to traverse."
254	)]
255	///
256	#[document_returns("The transformed structure wrapped in the applicative context.")]
257	#[document_examples]
258	///
259	/// ```
260	/// use fp_library::{
261	/// 	brands::*,
262	/// 	functions::*,
263	/// };
264	///
265	/// let x: Result<i32, i32> = Err(3);
266	/// let y = traverse_left::<ResultBrand, _, _, _, OptionBrand>(|e: i32| Some(e + 1), x);
267	/// assert_eq!(y, Some(Err(4)));
268	/// ```
269	pub fn traverse_left<
270		'a,
271		Brand: Bitraversable,
272		A: 'a + Clone,
273		B: 'a + Clone,
274		C: 'a + Clone,
275		F: Applicative,
276	>(
277		f: impl Fn(A) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>) + 'a,
278		p: Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
279	) -> 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, B>)>)
280	{
281		Brand::bi_traverse::<A, B, C, B, F>(f, |b| F::pure(b), p)
282	}
283
284	/// Traverses only the second-position elements, leaving first-position elements unchanged via `pure`.
285	///
286	/// Equivalent to `bi_traverse(pure, g, p)`.
287	#[document_signature]
288	///
289	#[document_type_parameters(
290		"The lifetime of the values.",
291		"The brand of the bitraversable structure.",
292		"The type of the first-position elements (unchanged).",
293		"The type of the second-position elements.",
294		"The output type for second-position elements.",
295		"The applicative context."
296	)]
297	///
298	#[document_parameters(
299		"The function for second-position elements.",
300		"The bitraversable structure to traverse."
301	)]
302	///
303	#[document_returns("The transformed structure wrapped in the applicative context.")]
304	#[document_examples]
305	///
306	/// ```
307	/// use fp_library::{
308	/// 	brands::*,
309	/// 	functions::*,
310	/// };
311	///
312	/// let x: Result<i32, i32> = Ok(5);
313	/// let y = traverse_right::<ResultBrand, _, _, _, OptionBrand>(|s: i32| Some(s * 2), x);
314	/// assert_eq!(y, Some(Ok(10)));
315	/// ```
316	pub fn traverse_right<
317		'a,
318		Brand: Bitraversable,
319		A: 'a + Clone,
320		B: 'a + Clone,
321		D: 'a + Clone,
322		F: Applicative,
323	>(
324		g: impl Fn(B) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, D>) + 'a,
325		p: Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
326	) -> 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, A, D>)>)
327	{
328		Brand::bi_traverse::<A, B, A, D, F>(|a| F::pure(a), g, p)
329	}
330
331	/// Traverses the bitraversable structure with arguments flipped.
332	///
333	/// Equivalent to `bi_traverse(f, g, p)` with the structure argument first.
334	#[document_signature]
335	///
336	#[document_type_parameters(
337		"The lifetime of the values.",
338		"The brand of the bitraversable structure.",
339		"The type of the first-position elements.",
340		"The type of the second-position elements.",
341		"The output type for first-position elements.",
342		"The output type for second-position elements.",
343		"The applicative context."
344	)]
345	///
346	#[document_parameters(
347		"The bitraversable structure to traverse.",
348		"The function for first-position elements.",
349		"The function for second-position elements."
350	)]
351	///
352	#[document_returns("The transformed structure wrapped in the applicative context.")]
353	#[document_examples]
354	///
355	/// ```
356	/// use fp_library::{
357	/// 	brands::*,
358	/// 	functions::*,
359	/// };
360	///
361	/// let x: Result<i32, i32> = Ok(5);
362	/// let y = bi_for::<ResultBrand, _, _, _, _, OptionBrand>(
363	/// 	x,
364	/// 	|e: i32| Some(e + 1),
365	/// 	|s: i32| Some(s * 2),
366	/// );
367	/// assert_eq!(y, Some(Ok(10)));
368	/// ```
369	pub fn bi_for<
370		'a,
371		Brand: Bitraversable,
372		A: 'a + Clone,
373		B: 'a + Clone,
374		C: 'a + Clone,
375		D: 'a + Clone,
376		F: Applicative,
377	>(
378		p: Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
379		f: impl Fn(A) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>) + 'a,
380		g: impl Fn(B) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, D>) + 'a,
381	) -> 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>)>)
382	{
383		Brand::bi_traverse::<A, B, C, D, F>(f, g, p)
384	}
385
386	/// Traverses only the first-position elements with arguments flipped.
387	///
388	/// Equivalent to `traverse_left(f, p)` with the structure argument first.
389	#[document_signature]
390	///
391	#[document_type_parameters(
392		"The lifetime of the values.",
393		"The brand of the bitraversable structure.",
394		"The type of the first-position elements.",
395		"The type of the second-position elements (unchanged).",
396		"The output type for first-position elements.",
397		"The applicative context."
398	)]
399	///
400	#[document_parameters(
401		"The bitraversable structure to traverse.",
402		"The function for first-position elements."
403	)]
404	///
405	#[document_returns("The transformed structure wrapped in the applicative context.")]
406	#[document_examples]
407	///
408	/// ```
409	/// use fp_library::{
410	/// 	brands::*,
411	/// 	functions::*,
412	/// };
413	///
414	/// let x: Result<i32, i32> = Err(3);
415	/// let y = for_left::<ResultBrand, _, _, _, OptionBrand>(x, |e: i32| Some(e + 1));
416	/// assert_eq!(y, Some(Err(4)));
417	/// ```
418	pub fn for_left<
419		'a,
420		Brand: Bitraversable,
421		A: 'a + Clone,
422		B: 'a + Clone,
423		C: 'a + Clone,
424		F: Applicative,
425	>(
426		p: Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
427		f: impl Fn(A) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>) + 'a,
428	) -> 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, B>)>)
429	{
430		Brand::bi_traverse::<A, B, C, B, F>(f, |b| F::pure(b), p)
431	}
432
433	/// Traverses only the second-position elements with arguments flipped.
434	///
435	/// Equivalent to `traverse_right(g, p)` with the structure argument first.
436	#[document_signature]
437	///
438	#[document_type_parameters(
439		"The lifetime of the values.",
440		"The brand of the bitraversable structure.",
441		"The type of the first-position elements (unchanged).",
442		"The type of the second-position elements.",
443		"The output type for second-position elements.",
444		"The applicative context."
445	)]
446	///
447	#[document_parameters(
448		"The bitraversable structure to traverse.",
449		"The function for second-position elements."
450	)]
451	///
452	#[document_returns("The transformed structure wrapped in the applicative context.")]
453	#[document_examples]
454	///
455	/// ```
456	/// use fp_library::{
457	/// 	brands::*,
458	/// 	functions::*,
459	/// };
460	///
461	/// let x: Result<i32, i32> = Ok(5);
462	/// let y = for_right::<ResultBrand, _, _, _, OptionBrand>(x, |s: i32| Some(s * 2));
463	/// assert_eq!(y, Some(Ok(10)));
464	/// ```
465	pub fn for_right<
466		'a,
467		Brand: Bitraversable,
468		A: 'a + Clone,
469		B: 'a + Clone,
470		D: 'a + Clone,
471		F: Applicative,
472	>(
473		p: Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
474		g: impl Fn(B) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, D>) + 'a,
475	) -> 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, A, D>)>)
476	{
477		Brand::bi_traverse::<A, B, A, D, F>(|a| F::pure(a), g, p)
478	}
479}
480
481pub use inner::*;