mini_kanren/core/
substitution.rs1use super::value::Value;
6use crate::core::logic_variable::Var;
7use crate::core::structure::Structure;
8use std::borrow::Cow;
9use std::collections::HashMap;
10use std::fmt::Formatter;
11
12#[derive(Clone, PartialEq)]
14pub struct Substitution<'s> {
15 pub(crate) subs: Cow<'s, HashMap<Var, Value>>,
16}
17
18impl Default for Substitution<'_> {
19 fn default() -> Self {
20 Self::empty()
21 }
22}
23
24impl<'s> Substitution<'s> {
25 pub fn empty() -> Self {
27 Substitution {
28 subs: Cow::Owned(HashMap::new()),
29 }
30 }
31
32 pub fn n_subs(&self) -> usize {
34 self.subs.len()
35 }
36
37 pub fn walk<'a>(&'a self, v: &'a Value) -> &'a Value {
42 if let Some(var) = v.try_as_var() {
43 if let Some(next) = self.subs.get(&var) {
44 return self.walk(next);
45 }
46 }
47 v
48 }
49
50 pub fn walk_star(&self, v: &Value) -> Value {
52 self.walk(v).walk_star(self)
53 }
54
55 pub fn extend(mut self, x: Var, v: Value) -> Option<Self> {
60 if self.occurs(&x, &v) {
61 None
62 } else {
63 self.subs.to_mut().insert(x, v);
64 Some(self)
65 }
66 }
67
68 pub fn occurs(&self, x: &Var, v: &Value) -> bool {
71 let v = self.walk(v);
72 v.occurs(x, self)
73 }
74
75 pub fn unify(self, u: &Value, v: &Value) -> Option<Self> {
77 let u = self.walk(u);
78 let v = self.walk(v);
79
80 if let Some(u) = u.try_as_var() {
81 return u.unify(&v.clone(), self);
82 }
83
84 if let Some(v) = v.try_as_var() {
85 return v.unify(&u.clone(), self);
86 }
87
88 let u = u.clone();
89 let v = v.clone();
90 u.unify(&v, self)
91 }
92
93 pub fn reify_s(self, v: &Value) -> Self {
95 self.walk(v).clone().reify_s(self)
96 }
97
98 pub fn reify(&self, v: &Value) -> Value {
101 let v = self.walk_star(v);
102 let r = Substitution::empty().reify_s(&v);
103 r.walk_star(&v)
104 }
105}
106
107impl std::fmt::Debug for Substitution<'_> {
108 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
109 write!(f, "{{")?;
110 let mut iter = self.subs.iter();
111 if let Some((var, val)) = iter.next() {
112 write!(f, "{:?}: {:?}", var, val)?;
113 }
114 for (var, val) in iter {
115 write!(f, ", {:?}: {:?}", var, val)?;
116 }
117 write!(f, "}}")
118 }
119}
120
121#[macro_export]
123macro_rules! substitution {
124 () => {Substitution{ subs: Cow::Owned(HashMap::new())}};
125
126 ($($var:ident : $val:expr),*) => {{
127 let mut subs = HashMap::new();
128 $(
129 subs.insert($var.clone(), Value::from($val.clone()));
130 )*
131 Substitution {
132 subs: Cow::Owned(subs)
133 }
134 }}
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 fn walk(v: Var, s: &Substitution) -> Value {
142 s.walk(&Value::var(v)).clone()
143 }
144
145 #[test]
146 fn it_works() {
147 let v = Var::new("v");
148 let w = Var::new("w");
149 let x = Var::new("x");
150 let y = Var::new("y");
151 let z = Var::new("z");
152
153 assert_eq!(walk(z, &substitution! {z: "a", x: w, y: z}), "a");
154 assert_eq!(walk(y, &substitution! {z: "a", x: w, y: z}), "a");
155 assert_eq!(walk(x, &substitution! {z: "a", x: w, y: z}), w);
156 assert_eq!(walk(x, &substitution! {x: y, v: x, w: x}), y);
157 assert_eq!(walk(v, &substitution! {x: y, v: x, w: x}), y);
158 assert_eq!(walk(w, &substitution! {x: y, v: x, w: x}), y);
159
160 assert_eq!(
161 substitution! {x: "b", z: y, w: vec![Value::from(x), "e".into(), (z).into()]}
162 .walk_star(&w.clone().into()),
163 Value::from(vec![Value::from("b"), "e".into(), y.clone().into()])
164 );
165 }
166
167 #[test]
168 fn unify_same_var_does_not_modify_substitution() {
169 let var_as_val = Var::new("x").into();
170 let sub = Substitution::empty().unify(&var_as_val, &var_as_val);
171 assert_eq!(sub, Some(Substitution::empty()));
172 }
173
174 #[test]
175 fn unify_two_vars_extends_substitution() {
176 let x = Var::new("x");
177 let y = Var::new("y");
178 let sub = Substitution::empty().unify(&x.into(), &y.into()).unwrap();
179 let expected = Substitution::empty().extend(x, y.into()).unwrap();
180 assert_eq!(sub, expected);
181 }
182
183 #[test]
184 fn unify_value_with_var_extends_substitution() {
185 let x = Var::new("x");
186 let v = Value::new(0);
187 let sub = Substitution::empty().unify(&v, &x.into()).unwrap();
188 let expected = Substitution::empty().extend(x, v).unwrap();
189 assert_eq!(sub, expected);
190 }
191
192 #[test]
193 fn unify_same_values_does_not_modify_substitution() {
194 let sub = Substitution::empty().unify(&Value::new(42), &Value::new(42));
195 assert_eq!(sub, Some(Substitution::empty()));
196 }
197
198 #[test]
199 fn unify_different_values_fails() {
200 let sub = Substitution::empty().unify(&Value::new(1), &Value::new(2));
201 assert_eq!(sub, None);
202 }
203}