Skip to main content

pattern_core/pattern/
comonad.rs

1//! Comonad operations for Pattern.
2//!
3//! This module provides comonad operations (`extract` and `extend`) that work with Pattern's
4//! "decorated sequence" semantics, where the value decorates the elements.
5//!
6//! # Decorated Sequence Semantics
7//!
8//! Pattern is fundamentally a "decorated sequence":
9//! - **Elements ARE the pattern** - the actual content (e.g., `["A", "B", "A"]`)
10//! - **Value DECORATES the elements** - provides information about them (e.g., `"sonata"`)
11//!
12//! This is what makes Pattern a natural Comonad:
13//! - `extract`: Access the decorative information (the value)
14//! - `extend`: Compute new decorative information based on context (the subpattern)
15//!
16//! # Examples
17//!
18//! ```
19//! use pattern_core::Pattern;
20//!
21//! // Create a pattern
22//! let p = Pattern::point("root");
23//!
24//! // Extract the decorative value
25//! assert_eq!(p.extract(), &"root");
26//!
27//! // Compute new decorations based on context
28//! let depths = p.extend(&|subpattern| subpattern.depth());
29//! assert_eq!(depths.extract(), &0); // Atomic pattern has depth 0
30//! ```
31
32use crate::Pattern;
33
34impl<V> Pattern<V> {
35    /// Extracts the decorative value at the current position.
36    ///
37    /// In Pattern's "decorated sequence" semantics, the value provides information
38    /// ABOUT the elements (the actual content). This operation accesses that decorative
39    /// information.
40    ///
41    /// # Returns
42    ///
43    /// A reference to the value field (the decoration).
44    ///
45    /// # Complexity
46    ///
47    /// Time: O(1), Space: O(1)
48    ///
49    /// # Examples
50    ///
51    /// ```
52    /// use pattern_core::Pattern;
53    ///
54    /// let p = Pattern::point(42);
55    /// assert_eq!(p.extract(), &42);
56    ///
57    /// let p = Pattern::pattern("root", vec![
58    ///     Pattern::point("a"),
59    ///     Pattern::point("b")
60    /// ]);
61    /// assert_eq!(p.extract(), &"root");
62    /// ```
63    #[inline]
64    pub fn extract(&self) -> &V {
65        &self.value
66    }
67
68    /// Computes new decorative information at each position based on subpattern context.
69    ///
70    /// This is the key Comonad operation. It takes a context-aware function that receives
71    /// the full subpattern at each position and computes new decorative information.
72    ///
73    /// The function `f` is called with the entire subpattern (not just the value), enabling
74    /// context-aware computation of new decorations.
75    ///
76    /// # Type Parameters
77    ///
78    /// - `W`: The type of new decorative values
79    /// - `F`: The function type (must be `Fn(&Pattern<V>) -> W`)
80    ///
81    /// # Arguments
82    ///
83    /// - `f`: Context-aware function that computes new decoration from subpattern
84    ///
85    /// # Returns
86    ///
87    /// A new pattern with the same structure, but decorated with computed values.
88    ///
89    /// # Complexity
90    ///
91    /// Time: O(n) where n = node count, Space: O(n)
92    ///
93    /// # Examples
94    ///
95    /// ```
96    /// use pattern_core::Pattern;
97    ///
98    /// let p = Pattern::pattern("root", vec![
99    ///     Pattern::pattern("a", vec![Pattern::point("x")]),
100    ///     Pattern::point("b")
101    /// ]);
102    ///
103    /// // Decorate each position with its depth
104    /// let depths = p.extend(&|subpattern| subpattern.depth());
105    /// assert_eq!(depths.extract(), &2); // root has depth 2
106    /// assert_eq!(depths.elements()[0].extract(), &1); // "a" has depth 1
107    /// ```
108    ///
109    /// # Comonad Laws
110    ///
111    /// This operation satisfies the Comonad laws:
112    ///
113    /// 1. **Left Identity**: `extract(extend(f, p)) == f(p)`
114    /// 2. **Right Identity**: `extend(extract, p) == p`
115    /// 3. **Associativity**: `extend(f, extend(g, p)) == extend(f ∘ extend(g), p)`
116    pub fn extend<W, F>(&self, f: &F) -> Pattern<W>
117    where
118        F: Fn(&Pattern<V>) -> W,
119    {
120        Pattern {
121            value: f(self),
122            elements: self.elements.iter().map(|elem| elem.extend(f)).collect(),
123        }
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130
131    #[test]
132    fn extract_returns_value() {
133        let p = Pattern::point(42);
134        assert_eq!(p.extract(), &42);
135    }
136
137    #[test]
138    fn extend_applies_function_at_all_positions() {
139        let p = Pattern::pattern(1, vec![Pattern::point(2), Pattern::point(3)]);
140
141        // Count nodes at each position
142        let sizes = p.extend(&|subp: &Pattern<i32>| subp.size());
143        assert_eq!(*sizes.extract(), 3); // root has 3 nodes
144        assert_eq!(*sizes.elements()[0].extract(), 1); // first child has 1 node
145        assert_eq!(*sizes.elements()[1].extract(), 1); // second child has 1 node
146    }
147}