Skip to main content

karpal_core/
comonad_traced.rs

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