Skip to main content

karpal_core/
comonad_traced.rs

1use crate::hkt::HKT;
2#[cfg(any(feature = "std", feature = "alloc"))]
3use crate::hkt::TracedF;
4use crate::monoid::Monoid;
5
6/// ComonadTraced: a comonad with a monoidal trace/accumulator.
7///
8/// Laws:
9/// - `trace(M::empty(), wa) == extract(wa)` (tracing with identity is extract)
10pub trait ComonadTraced<M: Monoid>: HKT {
11    fn trace<A>(m: M, wa: &Self::Of<A>) -> A;
12
13    /// Extract the focused value (equivalent to `trace(M::empty(), wa)`).
14    fn extract<A>(wa: &Self::Of<A>) -> A {
15        Self::trace(M::empty(), wa)
16    }
17}
18
19#[cfg(any(feature = "std", feature = "alloc"))]
20impl<M: Monoid + Clone + 'static> ComonadTraced<M> for TracedF<M> {
21    fn trace<A>(m: M, wa: &Box<dyn Fn(M) -> A>) -> A {
22        wa(m)
23    }
24}
25
26#[cfg(test)]
27mod tests {
28    use super::*;
29
30    #[test]
31    fn traced_trace() {
32        let w: Box<dyn Fn(i32) -> String> = Box::new(|m| format!("traced_{}", m));
33        assert_eq!(TracedF::<i32>::trace(5, &w), "traced_5");
34    }
35
36    #[test]
37    fn traced_extract() {
38        let w: Box<dyn Fn(i32) -> String> = Box::new(|m| format!("traced_{}", m));
39        // i32::empty() == 0
40        assert_eq!(TracedF::<i32>::extract(&w), "traced_0");
41    }
42}
43
44#[cfg(test)]
45mod law_tests {
46    use super::*;
47    use proptest::prelude::*;
48
49    proptest! {
50        // trace(M::empty(), wa) == extract(wa)
51        #[test]
52        fn traced_identity_trace(offset in any::<i16>()) {
53            let w: Box<dyn Fn(i32) -> i32> = Box::new(move |m| m + offset as i32);
54            let left = TracedF::<i32>::trace(i32::empty(), &w);
55            let right = TracedF::<i32>::extract(&w);
56            prop_assert_eq!(left, right);
57        }
58    }
59}