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