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