Skip to main content

karpal_core/
comonad_env.rs

1use crate::comonad::Comonad;
2use crate::hkt::EnvF;
3
4/// ComonadEnv: a Comonad with access to an environment value.
5///
6/// Laws:
7/// - `extract(local(wa, f)) == extract(wa)` (local doesn't change the focus)
8pub trait ComonadEnv<E>: Comonad {
9    fn ask<A>(wa: &Self::Of<A>) -> E;
10    fn local<A>(wa: Self::Of<A>, f: impl Fn(E) -> E) -> Self::Of<A>;
11}
12
13impl<E: Clone> ComonadEnv<E> for EnvF<E> {
14    fn ask<A>(wa: &(E, A)) -> E {
15        wa.0.clone()
16    }
17
18    fn local<A>(wa: (E, A), f: impl Fn(E) -> E) -> (E, A) {
19        (f(wa.0), wa.1)
20    }
21}
22
23#[cfg(test)]
24mod tests {
25    use super::*;
26
27    #[test]
28    fn env_ask() {
29        let w = ("hello", 42);
30        assert_eq!(EnvF::<&str>::ask(&w), "hello");
31    }
32
33    #[test]
34    fn env_local() {
35        let w = (10i32, "value");
36        let result = EnvF::<i32>::local(w, |e| e * 2);
37        assert_eq!(result, (20, "value"));
38    }
39}
40
41#[cfg(test)]
42mod law_tests {
43    use super::*;
44    use proptest::prelude::*;
45
46    proptest! {
47        // extract(local(wa, f)) == extract(wa)
48        #[test]
49        fn env_local_preserves_extract(e in any::<i16>(), a in any::<i32>()) {
50            let w = (e, a);
51            let localed = EnvF::<i16>::local(w, |e| e.wrapping_add(1));
52            let left = EnvF::<i16>::extract(&localed);
53            let right = EnvF::<i16>::extract(&(e, a));
54            prop_assert_eq!(left, right);
55        }
56    }
57}