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