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