fp_library/classes/alternative.rs
1//! Alternative functors, combining [`Applicative`](crate::classes::Applicative) and [`Plus`](crate::classes::Plus).
2//!
3//! `Alternative` provides the ability to choose between computations (via [`Alt`](crate::classes::Alt))
4//! with an identity element (via [`Plus`](crate::classes::Plus)), combined with applicative
5//! lifting (via [`Applicative`](crate::classes::Applicative)).
6//!
7//! ### Examples
8//!
9//! ```
10//! use fp_library::{
11//! brands::*,
12//! classes::*,
13//! functions::*,
14//! };
15//!
16//! // guard filters based on a condition
17//! let result: Vec<()> = guard::<VecBrand>(true);
18//! assert_eq!(result, vec![()]);
19//!
20//! let result: Vec<()> = guard::<VecBrand>(false);
21//! assert_eq!(result, vec![]);
22//! ```
23
24#[fp_macros::document_module]
25mod inner {
26 use {
27 crate::{
28 classes::*,
29 kinds::*,
30 },
31 fp_macros::*,
32 };
33
34 /// A type class combining [`Applicative`] and [`Plus`].
35 ///
36 /// `Alternative` has no members of its own; it specifies that the type
37 /// constructor has both [`Applicative`] and [`Plus`] instances.
38 ///
39 /// ### Laws
40 ///
41 /// `Alternative` instances must satisfy the following laws:
42 /// * Distributivity: `apply(alt(f, g), x) = alt(apply(f, x), apply(g, x))`.
43 /// * Annihilation: `apply(empty, f) = empty`.
44 #[document_examples]
45 ///
46 /// Alternative laws for [`Vec`]:
47 ///
48 /// ```
49 /// use fp_library::{
50 /// brands::*,
51 /// classes::*,
52 /// functions::*,
53 /// };
54 ///
55 /// // Annihilation: apply(empty, f) = empty
56 /// let f: Vec<i32> = vec![1, 2, 3];
57 /// let empty_fns: Vec<std::rc::Rc<dyn Fn(i32) -> i32>> = plus_empty::<VecBrand, _>();
58 /// let result = apply(empty_fns, f);
59 /// assert_eq!(result, plus_empty::<VecBrand, i32>());
60 /// ```
61 pub trait Alternative: Applicative + Plus {}
62
63 /// Blanket implementation of [`Alternative`].
64 #[document_type_parameters("The brand type.")]
65 impl<Brand> Alternative for Brand where Brand: Applicative + Plus {}
66
67 /// Fails using [`Plus`] if a condition does not hold, or succeeds using
68 /// [`Applicative`] if it does.
69 ///
70 /// This is useful in monadic/applicative comprehensions to filter results.
71 #[document_signature]
72 ///
73 #[document_type_parameters(
74 "The lifetime of the values.",
75 "The brand of the alternative functor."
76 )]
77 ///
78 #[document_parameters("The condition to check.")]
79 ///
80 #[document_returns("`pure(())` if the condition is `true`, `empty` otherwise.")]
81 #[document_examples]
82 ///
83 /// ```
84 /// use fp_library::{
85 /// brands::*,
86 /// classes::*,
87 /// functions::*,
88 /// };
89 ///
90 /// // Using guard to filter in a Vec comprehension
91 /// let result: Vec<()> = guard::<VecBrand>(true);
92 /// assert_eq!(result, vec![()]);
93 ///
94 /// let result: Vec<()> = guard::<VecBrand>(false);
95 /// assert_eq!(result, vec![]);
96 /// ```
97 pub fn guard<'a, Brand: Alternative>(
98 condition: bool
99 ) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ()>) {
100 if condition { Brand::pure(()) } else { Brand::empty() }
101 }
102}
103
104pub use inner::*;