Skip to main content

karpal_core/
divisible.rs

1use crate::contravariant::PredicateF;
2use crate::divide::Divide;
3
4/// Divisible: the contravariant analogue of Applicative.
5///
6/// Adds a `conquer` operation (the identity for `divide`), analogous to `pure`.
7///
8/// Laws:
9/// - Left identity: `divide(f, conquer(), fa) ≈ contramap(snd . f, fa)`
10/// - Right identity: `divide(f, fa, conquer()) ≈ contramap(fst . f, fa)`
11pub trait Divisible: Divide {
12    fn conquer<A: 'static>() -> Self::Of<A>;
13}
14
15impl Divisible for PredicateF {
16    fn conquer<A: 'static>() -> Box<dyn Fn(A) -> bool> {
17        Box::new(|_| true)
18    }
19}
20
21#[cfg(test)]
22mod tests {
23    use super::*;
24
25    #[test]
26    fn predicate_conquer() {
27        let p: Box<dyn Fn(i32) -> bool> = PredicateF::conquer();
28        assert!(p(42));
29        assert!(p(-1));
30        assert!(p(0));
31    }
32}
33
34#[cfg(test)]
35mod law_tests {
36    use super::*;
37    use proptest::prelude::*;
38
39    proptest! {
40        // Left identity: divide(|a| ((), a), conquer(), fa)(x) == fa(x)
41        #[test]
42        fn predicate_left_identity(x in any::<i32>()) {
43            let fa: Box<dyn Fn(i32) -> bool> = Box::new(|a| a > 0);
44            let expected = fa(x);
45
46            let fa2: Box<dyn Fn(i32) -> bool> = Box::new(|a| a > 0);
47            let result = PredicateF::divide(
48                |a: i32| ((), a),
49                PredicateF::conquer::<()>(),
50                fa2,
51            );
52            prop_assert_eq!(result(x), expected);
53        }
54
55        // Right identity: divide(|a| (a, ()), fa, conquer())(x) == fa(x)
56        #[test]
57        fn predicate_right_identity(x in any::<i32>()) {
58            let fa: Box<dyn Fn(i32) -> bool> = Box::new(|a| a > 0);
59            let expected = fa(x);
60
61            let fa2: Box<dyn Fn(i32) -> bool> = Box::new(|a| a > 0);
62            let result = PredicateF::divide(
63                |a: i32| (a, ()),
64                fa2,
65                PredicateF::conquer::<()>(),
66            );
67            prop_assert_eq!(result(x), expected);
68        }
69    }
70}