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::*;