Skip to main content

mini_kanren/core/
logic_variable.rs

1use std::sync::atomic::{AtomicUsize, Ordering};
2
3static VAR_COUNTER: AtomicUsize = AtomicUsize::new(0);
4
5/// Named logic variable.
6///
7/// The variable name is purely descriptive to help understanding.
8/// Any newly created variables is different from all previously
9/// created variables, even if they have the same name.
10/// However, variables can be copied, which preserves identity.
11#[derive(Copy, Clone, PartialEq, Eq, Hash)]
12pub struct Var {
13    name: &'static str,
14    id: usize,
15}
16
17impl Var {
18    /// Create a new unique logic variable.
19    pub fn new(name: &'static str) -> Self {
20        // The C++ reference says: "typical use for relaxed memory is incrementing counters"
21        let id = VAR_COUNTER.fetch_add(1, Ordering::Relaxed);
22        Var { name, id }
23    }
24
25    /// Return the variable's name.
26    pub fn name(&self) -> &str {
27        self.name
28    }
29}
30
31impl From<&'static str> for Var {
32    fn from(name: &'static str) -> Self {
33        Var::new(name)
34    }
35}
36
37impl std::fmt::Debug for Var {
38    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
39        write!(f, "{}", self.name)
40    }
41}
42
43/// Reified logic variable.
44///
45/// Reified variables represent logic variables that remain fresh
46/// after goals have run.
47#[derive(Copy, Clone, PartialEq, Eq)]
48pub struct ReifiedVar(pub usize);
49
50impl std::fmt::Debug for ReifiedVar {
51    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
52        write!(f, "_{}", self.0)
53    }
54}
55
56#[cfg(test)]
57mod unit_tests {
58    use super::*;
59
60    #[test]
61    fn can_create_fresh_variables_with_name() {
62        let var = Var::new("x");
63        assert_eq!(var.name(), "x");
64    }
65
66    #[test]
67    fn can_copy_variables() {
68        let var_a = Var::new("x");
69        let var_b = var_a;
70        assert_eq!(var_a.name(), var_b.name());
71    }
72
73    #[test]
74    fn copied_variables_are_equal() {
75        let var_a = Var::new("x");
76        let var_b = var_a;
77        assert_eq!(var_a, var_b);
78    }
79
80    #[test]
81    fn two_variables_with_same_name_are_not_equal() {
82        let var_a = Var::new("x");
83        let var_b = Var::new("x");
84        assert_ne!(var_a, var_b);
85    }
86
87    #[test]
88    fn can_convert_str_to_var() {
89        let var: Var = "foo".into();
90        assert_eq!(var.name(), "foo");
91    }
92}