Skip to main content

karpal_core/
comonad_env.rs

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