Skip to main content

fp_library/classes/
alt.rs

1//! Choosing between values in a context, associatively.
2//!
3//! `Alt` is to type constructors as [`Semigroup`](crate::classes::Semigroup) is to concrete types.
4//!
5//! ### Examples
6//!
7//! ```
8//! use fp_library::{
9//! 	brands::*,
10//! 	classes::*,
11//! 	functions::*,
12//! };
13//!
14//! let x: Option<i32> = None;
15//! let y = Some(5);
16//! let z = alt::<OptionBrand, _>(x, y);
17//! assert_eq!(z, Some(5));
18//! ```
19
20#[fp_macros::document_module]
21mod inner {
22	use {
23		crate::{
24			classes::*,
25			kinds::*,
26		},
27		fp_macros::*,
28	};
29
30	/// A type class for associative choice on type constructors.
31	///
32	/// `Alt` is similar to [`Semigroup`], except that it applies to types of
33	/// kind `* -> *` (like `Option` or `Vec`) rather than concrete types
34	/// (like `String` or `i32`).
35	///
36	/// A common use case is to select the first "valid" item, or, if all items
37	/// are "invalid", fall back to the last item.
38	///
39	/// ### Laws
40	///
41	/// `Alt` instances must satisfy the following laws:
42	/// * Associativity: `alt(alt(x, y), z) = alt(x, alt(y, z))`.
43	/// * Distributivity: `map(f, alt(x, y)) = alt(map(f, x), map(f, y))`.
44	#[document_examples]
45	///
46	/// Alt laws for [`Option`]:
47	///
48	/// ```
49	/// use fp_library::{
50	/// 	brands::*,
51	/// 	classes::*,
52	/// 	functions::*,
53	/// };
54	///
55	/// // Associativity: alt(alt(x, y), z) = alt(x, alt(y, z))
56	/// let x: Option<i32> = None;
57	/// let y = Some(1);
58	/// let z = Some(2);
59	/// assert_eq!(
60	/// 	alt::<OptionBrand, _>(alt::<OptionBrand, _>(x, y), z),
61	/// 	alt::<OptionBrand, _>(x, alt::<OptionBrand, _>(y, z)),
62	/// );
63	///
64	/// // Distributivity: map(f, alt(x, y)) = alt(map(f, x), map(f, y))
65	/// let f = |i: i32| i * 2;
66	/// let x = Some(3);
67	/// let y: Option<i32> = None;
68	/// assert_eq!(
69	/// 	map::<OptionBrand, _, _>(f, alt::<OptionBrand, _>(x, y)),
70	/// 	alt::<OptionBrand, _>(map::<OptionBrand, _, _>(f, x), map::<OptionBrand, _, _>(f, y)),
71	/// );
72	/// ```
73	pub trait Alt: Functor {
74		/// Chooses between two values in a context.
75		///
76		/// This method provides an associative binary operation on type constructors.
77		/// For `Option`, this returns the first `Some` value. For `Vec`, this
78		/// concatenates the two vectors.
79		#[document_signature]
80		///
81		#[document_type_parameters(
82			"The lifetime of the values.",
83			"The type of the value inside the context."
84		)]
85		///
86		#[document_parameters("The first value.", "The second value.")]
87		///
88		#[document_returns("The chosen/combined value.")]
89		#[document_examples]
90		///
91		/// ```
92		/// use fp_library::{
93		/// 	brands::*,
94		/// 	classes::*,
95		/// 	functions::*,
96		/// };
97		///
98		/// let x: Option<i32> = None;
99		/// let y = Some(5);
100		/// let z = alt::<OptionBrand, _>(x, y);
101		/// assert_eq!(z, Some(5));
102		/// ```
103		fn alt<'a, A: 'a>(
104			fa1: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
105			fa2: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
106		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>);
107	}
108
109	/// Chooses between two values in a context.
110	///
111	/// Free function version that dispatches to [the type class' associated function][`Alt::alt`].
112	#[document_signature]
113	///
114	#[document_type_parameters(
115		"The lifetime of the values.",
116		"The brand of the context.",
117		"The type of the value inside the context."
118	)]
119	///
120	#[document_parameters("The first value.", "The second value.")]
121	///
122	#[document_returns("The chosen/combined value.")]
123	#[document_examples]
124	///
125	/// ```
126	/// use fp_library::{
127	/// 	brands::*,
128	/// 	classes::*,
129	/// 	functions::*,
130	/// };
131	///
132	/// let x: Option<i32> = None;
133	/// let y = Some(5);
134	/// let z = alt::<OptionBrand, _>(x, y);
135	/// assert_eq!(z, Some(5));
136	/// ```
137	pub fn alt<'a, Brand: Alt, A: 'a>(
138		fa1: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
139		fa2: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
140	) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>) {
141		Brand::alt(fa1, fa2)
142	}
143}
144
145pub use inner::*;