Skip to main content

karpal_core/
selective.rs

1use crate::applicative::Applicative;
2use crate::hkt::OptionF;
3
4/// Selective: an Applicative that can conditionally apply effects.
5///
6/// Laws:
7/// - Identity: `select(fmap(Right, x), _) == fmap(id, x)`
8///   (where Right means Ok variant)
9pub trait Selective: Applicative {
10    fn select<A, B, F>(fab: Self::Of<Result<A, B>>, ff: Self::Of<F>) -> Self::Of<B>
11    where
12        A: Clone,
13        F: Fn(A) -> B;
14}
15
16impl Selective for OptionF {
17    fn select<A, B, F>(fab: Option<Result<A, B>>, ff: Option<F>) -> Option<B>
18    where
19        A: Clone,
20        F: Fn(A) -> B,
21    {
22        match fab {
23            None => None,
24            Some(Ok(a)) => ff.map(|f| f(a)),
25            Some(Err(b)) => Some(b),
26        }
27    }
28}
29
30#[cfg(test)]
31mod tests {
32    use super::*;
33
34    #[test]
35    fn option_select_right() {
36        let result = OptionF::select(Some(Err(42i32)), Some(|_x: i32| 0));
37        assert_eq!(result, Some(42));
38    }
39
40    #[test]
41    fn option_select_left_with_fn() {
42        let result = OptionF::select(Some(Ok(3i32)), Some(|x: i32| x * 2));
43        assert_eq!(result, Some(6));
44    }
45
46    #[test]
47    fn option_select_left_no_fn() {
48        let result = OptionF::select(Some(Ok(3i32)), None::<fn(i32) -> i32>);
49        assert_eq!(result, None);
50    }
51
52    #[test]
53    fn option_select_none() {
54        let result = OptionF::select(None::<Result<i32, i32>>, Some(|x: i32| x * 2));
55        assert_eq!(result, None);
56    }
57}
58
59#[cfg(test)]
60mod law_tests {
61    use super::*;
62    use crate::functor::Functor;
63    use proptest::prelude::*;
64
65    proptest! {
66        // Identity: select(fmap(Err, x), _) == x
67        // When all values are Err (already resolved), the function is never applied
68        #[test]
69        fn option_identity(x in any::<Option<i32>>()) {
70            let fab = OptionF::fmap(x, |b| Err::<i32, i32>(b));
71            let dummy: Option<fn(i32) -> i32> = Some(|_| panic!("should not be called"));
72            let result = OptionF::select(fab, dummy);
73            prop_assert_eq!(result, x);
74        }
75    }
76}