Skip to main content

karpal_core/
alt.rs

1use crate::functor::Functor;
2use crate::hkt::OptionF;
3use crate::hkt::ResultF;
4#[cfg(any(feature = "std", feature = "alloc"))]
5use crate::hkt::VecF;
6
7/// Alt: a Functor with an associative choice operation.
8///
9/// Laws:
10/// - Associativity: `alt(alt(a, b), c) == alt(a, alt(b, c))`
11/// - Distributivity: `fmap(f, alt(a, b)) == alt(fmap(f, a), fmap(f, b))`
12pub trait Alt: Functor {
13    fn alt<A>(fa1: Self::Of<A>, fa2: Self::Of<A>) -> Self::Of<A>;
14}
15
16impl Alt for OptionF {
17    fn alt<A>(fa1: Option<A>, fa2: Option<A>) -> Option<A> {
18        fa1.or(fa2)
19    }
20}
21
22impl<E> Alt for ResultF<E> {
23    fn alt<A>(fa1: Result<A, E>, fa2: Result<A, E>) -> Result<A, E> {
24        fa1.or(fa2)
25    }
26}
27
28#[cfg(any(feature = "std", feature = "alloc"))]
29impl Alt for VecF {
30    fn alt<A>(mut fa1: Vec<A>, fa2: Vec<A>) -> Vec<A> {
31        fa1.extend(fa2);
32        fa1
33    }
34}
35
36#[cfg(any(feature = "std", feature = "alloc"))]
37impl Alt for crate::hkt::NonEmptyVecF {
38    fn alt<A>(
39        mut fa1: crate::hkt::NonEmptyVec<A>,
40        fa2: crate::hkt::NonEmptyVec<A>,
41    ) -> crate::hkt::NonEmptyVec<A> {
42        fa1.tail.push(fa2.head);
43        fa1.tail.extend(fa2.tail);
44        fa1
45    }
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    #[test]
53    fn option_alt_some_some() {
54        assert_eq!(OptionF::alt(Some(1), Some(2)), Some(1));
55    }
56
57    #[test]
58    fn option_alt_none_some() {
59        assert_eq!(OptionF::alt(None, Some(2)), Some(2));
60    }
61
62    #[test]
63    fn option_alt_none_none() {
64        assert_eq!(OptionF::alt(None::<i32>, None), None);
65    }
66
67    #[test]
68    fn result_alt() {
69        assert_eq!(ResultF::<&str>::alt(Err("a"), Ok(2)), Ok(2));
70        assert_eq!(ResultF::<&str>::alt(Ok(1), Ok(2)), Ok(1));
71    }
72
73    #[test]
74    fn vec_alt() {
75        assert_eq!(VecF::alt(vec![1, 2], vec![3, 4]), vec![1, 2, 3, 4]);
76    }
77}
78
79#[cfg(test)]
80mod law_tests {
81    use super::*;
82    use crate::functor::Functor;
83    use proptest::prelude::*;
84
85    proptest! {
86        // Associativity: alt(alt(a, b), c) == alt(a, alt(b, c))
87        #[test]
88        fn option_associativity(
89            a in any::<Option<i32>>(),
90            b in any::<Option<i32>>(),
91            c in any::<Option<i32>>()
92        ) {
93            let left = OptionF::alt(OptionF::alt(a, b), c);
94            let right = OptionF::alt(a, OptionF::alt(b, c));
95            prop_assert_eq!(left, right);
96        }
97
98        // Distributivity: fmap(f, alt(a, b)) == alt(fmap(f, a), fmap(f, b))
99        #[test]
100        fn option_distributivity(
101            a in any::<Option<i32>>(),
102            b in any::<Option<i32>>()
103        ) {
104            let f = |x: i32| x.wrapping_add(1);
105            let left = OptionF::fmap(OptionF::alt(a, b), f);
106            let right = OptionF::alt(OptionF::fmap(a, f), OptionF::fmap(b, f));
107            prop_assert_eq!(left, right);
108        }
109
110        #[test]
111        fn vec_associativity(
112            a in prop::collection::vec(any::<i32>(), 0..5),
113            b in prop::collection::vec(any::<i32>(), 0..5),
114            c in prop::collection::vec(any::<i32>(), 0..5)
115        ) {
116            let left = VecF::alt(VecF::alt(a.clone(), b.clone()), c.clone());
117            let right = VecF::alt(a, VecF::alt(b, c));
118            prop_assert_eq!(left, right);
119        }
120    }
121}