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::explicit::*,
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::explicit::*,
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, _, _, _>(
71	/// 		map::<OptionBrand, _, _, _, _>(f, x),
72	/// 		map::<OptionBrand, _, _, _, _>(f, y)
73	/// 	),
74	/// );
75	/// ```
76	pub trait Alt: Functor {
77		/// Chooses between two values in a context.
78		///
79		/// This method provides an associative binary operation on type constructors.
80		/// For `Option`, this returns the first `Some` value. For `Vec`, this
81		/// concatenates the two vectors.
82		#[document_signature]
83		///
84		#[document_type_parameters(
85			"The lifetime of the values.",
86			"The type of the value inside the context."
87		)]
88		///
89		#[document_parameters("The first value.", "The second value.")]
90		///
91		#[document_returns("The chosen/combined value.")]
92		#[document_examples]
93		///
94		/// ```
95		/// use fp_library::{
96		/// 	brands::*,
97		/// 	classes::*,
98		/// 	functions::explicit::*,
99		/// };
100		///
101		/// let x: Option<i32> = None;
102		/// let y = Some(5);
103		/// let z = alt::<OptionBrand, _, _, _>(x, y);
104		/// assert_eq!(z, Some(5));
105		/// ```
106		fn alt<'a, A: 'a>(
107			fa1: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
108			fa2: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
109		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>);
110	}
111
112	/// Chooses between two values in a context.
113	///
114	/// Free function version that dispatches to [the type class' associated function][`Alt::alt`].
115	#[document_signature]
116	///
117	#[document_type_parameters(
118		"The lifetime of the values.",
119		"The brand of the context.",
120		"The type of the value inside the context."
121	)]
122	///
123	#[document_parameters("The first value.", "The second value.")]
124	///
125	#[document_returns("The chosen/combined value.")]
126	#[document_examples]
127	///
128	/// ```
129	/// use fp_library::{
130	/// 	brands::*,
131	/// 	classes::*,
132	/// 	functions::explicit::*,
133	/// };
134	///
135	/// let x: Option<i32> = None;
136	/// let y = Some(5);
137	/// let z = alt::<OptionBrand, _, _, _>(x, y);
138	/// assert_eq!(z, Some(5));
139	/// ```
140	pub fn alt<'a, Brand: Alt, A: 'a>(
141		fa1: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
142		fa2: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
143	) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>) {
144		Brand::alt(fa1, fa2)
145	}
146}
147
148pub use inner::*;