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}