Skip to main content

karpal_core/
comonad_store.rs

1use crate::hkt::HKT;
2#[cfg(any(feature = "std", feature = "alloc"))]
3use crate::hkt::StoreF;
4#[cfg(all(not(feature = "std"), feature = "alloc"))]
5use alloc::boxed::Box;
6
7/// ComonadStore: a comonad with a notion of position and peeking.
8///
9/// Laws:
10/// - `peek(pos(wa), wa) == extract(wa)` (peeking at current position is extract)
11pub trait ComonadStore<S>: HKT {
12    fn pos<A>(wa: &Self::Of<A>) -> S;
13    fn peek<A>(s: S, wa: &Self::Of<A>) -> A;
14
15    /// Extract the focused value (equivalent to `peek(pos(wa), wa)`).
16    fn extract<A>(wa: &Self::Of<A>) -> A
17    where
18        S: Clone,
19    {
20        Self::peek(Self::pos(wa), wa)
21    }
22}
23
24#[cfg(any(feature = "std", feature = "alloc"))]
25impl<S: Clone + 'static> ComonadStore<S> for StoreF<S> {
26    fn pos<A>(wa: &(Box<dyn Fn(S) -> A>, S)) -> S {
27        wa.1.clone()
28    }
29
30    fn peek<A>(s: S, wa: &(Box<dyn Fn(S) -> A>, S)) -> A {
31        (wa.0)(s)
32    }
33}
34
35#[cfg(test)]
36mod tests {
37    use super::*;
38
39    #[test]
40    fn store_pos() {
41        let w: (Box<dyn Fn(i32) -> String>, i32) = (Box::new(|s| format!("value_{}", s)), 42);
42        assert_eq!(StoreF::<i32>::pos(&w), 42);
43    }
44
45    #[test]
46    fn store_peek() {
47        let w: (Box<dyn Fn(i32) -> String>, i32) = (Box::new(|s| format!("value_{}", s)), 42);
48        assert_eq!(StoreF::<i32>::peek(10, &w), "value_10");
49    }
50
51    #[test]
52    fn store_extract() {
53        let w: (Box<dyn Fn(i32) -> String>, i32) = (Box::new(|s| format!("value_{}", s)), 42);
54        assert_eq!(StoreF::<i32>::extract(&w), "value_42");
55    }
56}
57
58#[cfg(test)]
59mod law_tests {
60    use super::*;
61    use proptest::prelude::*;
62
63    proptest! {
64        // peek(pos(wa), wa) == extract(wa)
65        #[test]
66        fn store_peek_pos_is_extract(s in any::<i16>()) {
67            let w: (Box<dyn Fn(i16) -> i32>, i16) =
68                (Box::new(|s| s as i32 * 2), s);
69            let pos = StoreF::<i16>::pos(&w);
70            let left = StoreF::<i16>::peek(pos, &w);
71            let right = StoreF::<i16>::extract(&w);
72            prop_assert_eq!(left, right);
73        }
74    }
75}