Skip to main content

camxes_rs/
bindful.rs

1//! Monad for binding values to numbered variables
2//!
3//! Ported from: Bindful.hs
4//!
5//! This module provides a convenient abstraction for managing numbered variable
6//! bindings during semantic analysis. It handles:
7//! - Binding values to numbered variables
8//! - Finding the next free variable number
9//! - Scoped bindings with automatic cleanup
10//! - Modifying all bound values
11//!
12//! Original Haskell:
13//! ```haskell
14//! class Monad m => BindfulMonad s m | m -> s where
15//!     withBinding :: s -> (Int -> m r) -> m r
16//!     binding :: Int -> m s
17//!     twiddleBound :: (s -> s) -> m ()
18//!     getValues :: m [s]
19//!     evalBindful :: m a -> a
20//! ```
21
22use std::collections::HashMap;
23
24// Ported from: Bindful.hs :: Bindful
25/// Bindful state: maps variable numbers to values
26#[derive(Debug, Clone)]
27pub struct Bindful<S> {
28    bindings: HashMap<i32, S>,
29}
30
31impl<S: Clone> Bindful<S> {
32    // Ported from: Bindful.hs :: evalBindful
33    /// Create a new empty Bindful state
34    pub fn new() -> Self {
35        Bindful {
36            bindings: HashMap::new(),
37        }
38    }
39
40    // Ported from: Bindful.hs :: nextFree
41    /// Find the next free variable number
42    pub fn next_free(&self) -> i32 {
43        (1..)
44            .find(|n| !self.bindings.contains_key(n))
45            .unwrap_or(1)
46    }
47
48    // Ported from: Bindful.hs :: bind
49    /// Bind a value to a variable number
50    pub fn bind(&mut self, n: i32, value: S) {
51        self.bindings.insert(n, value);
52    }
53
54    // Ported from: Bindful.hs :: unbind
55    /// Unbind a variable number
56    pub fn unbind(&mut self, n: i32) {
57        self.bindings.remove(&n);
58    }
59
60    // Ported from: Bindful.hs :: binding
61    /// Get the value bound to a variable number
62    pub fn binding(&self, n: i32) -> Option<S> {
63        self.bindings.get(&n).cloned()
64    }
65
66    // Ported from: Bindful.hs :: getValues
67    /// Get all bound values
68    pub fn get_values(&self) -> Vec<S> {
69        self.bindings.values().cloned().collect()
70    }
71
72    // Ported from: Bindful.hs :: twiddleBound
73    /// Apply a function to all bound values
74    pub fn twiddle_bound<F>(&mut self, f: F)
75    where
76        F: Fn(&S) -> S,
77    {
78        let keys: Vec<i32> = self.bindings.keys().cloned().collect();
79        for n in keys {
80            if let Some(value) = self.bindings.get(&n).cloned() {
81                self.bindings.insert(n, f(&value));
82            }
83        }
84    }
85
86    // Ported from: Bindful.hs :: withBinding
87    /// Execute a function with a temporary binding, then unbind
88    pub fn with_binding<R, F>(&mut self, value: S, f: F) -> R
89    where
90        F: FnOnce(i32, &mut Self) -> R,
91    {
92        let n = self.next_free();
93        self.bind(n, value);
94        let result = f(n, self);
95        self.unbind(n);
96        result
97    }
98
99    // Ported from: Bindful.hs :: bindNext
100    /// Bind a value to the next free variable number
101    pub fn bind_next(&mut self, value: S) -> i32 {
102        let n = self.next_free();
103        self.bind(n, value);
104        n
105    }
106}
107
108impl<S: Clone> Default for Bindful<S> {
109    fn default() -> Self {
110        Self::new()
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117
118    #[test]
119    fn test_next_free() {
120        let bf: Bindful<String> = Bindful::new();
121        assert_eq!(bf.next_free(), 1);
122    }
123
124    #[test]
125    fn test_bind_and_binding() {
126        let mut bf = Bindful::new();
127        bf.bind(1, "hello".to_string());
128        assert_eq!(bf.binding(1), Some("hello".to_string()));
129        assert_eq!(bf.binding(2), None);
130    }
131
132    #[test]
133    fn test_unbind() {
134        let mut bf = Bindful::new();
135        bf.bind(1, "hello".to_string());
136        bf.unbind(1);
137        assert_eq!(bf.binding(1), None);
138    }
139
140    #[test]
141    fn test_with_binding() {
142        let mut bf = Bindful::new();
143        let result = bf.with_binding("temp".to_string(), |n, bf| {
144            assert_eq!(bf.binding(n), Some("temp".to_string()));
145            42
146        });
147        assert_eq!(result, 42);
148        assert_eq!(bf.binding(1), None); // Should be unbound after
149    }
150
151    #[test]
152    fn test_twiddle_bound() {
153        let mut bf = Bindful::new();
154        bf.bind(1, 10);
155        bf.bind(2, 20);
156        bf.twiddle_bound(|x| x + 5);
157        assert_eq!(bf.binding(1), Some(15));
158        assert_eq!(bf.binding(2), Some(25));
159    }
160
161    #[test]
162    fn test_get_values() {
163        let mut bf = Bindful::new();
164        bf.bind(1, "a".to_string());
165        bf.bind(2, "b".to_string());
166        let mut values = bf.get_values();
167        values.sort();
168        assert_eq!(values, vec!["a".to_string(), "b".to_string()]);
169    }
170}