Skip to main content

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