mini_kanren/goals/
hashmap.rs1use crate::core::structure::Structure;
2use crate::{Substitution, Value, Var};
3use std::any::Any;
4use std::collections::HashMap;
5use std::fmt::Debug;
6use std::hash::Hash;
7use std::sync::Arc;
8
9#[macro_export]
10macro_rules! hashmap {
11 () => {
12 std::collections::HashMap::new()
13 };
14}
15
16impl<T: 'static + Debug + Eq + Hash> Structure for HashMap<T, Value> {
17 fn occurs<'s>(&self, _x: &Var, _s: &Substitution<'s>) -> bool {
18 unimplemented!()
20 }
21
22 fn unify<'s>(&self, v: &Value, mut s: Substitution<'s>) -> Option<Substitution<'s>> {
23 let other = v.downcast_ref::<Self>()?;
24 for (_, v1, v2) in double_iterator(self, other) {
25 s = s.unify(v1?, v2?)?;
26 }
27 Some(s)
28 }
29
30 fn walk_star(self: Arc<Self>, _s: &Substitution<'_>) -> Value {
31 unimplemented!()
33 }
34
35 fn reify_s<'s>(&self, _s: Substitution<'s>) -> Substitution<'s> {
36 unimplemented!()
38 }
39
40 fn as_any(&self) -> &dyn Any {
41 self
42 }
43
44 fn eqv(&self, other: &Value) -> bool {
45 other
46 .downcast_ref::<Self>()
47 .map(|v| double_iterator(self, v).all(|(_, v1, v2)| v1 == v2))
48 .unwrap_or(false)
49 }
50}
51
52fn double_iterator<'a, K: Eq + Hash, V>(
53 first: &'a HashMap<K, V>,
54 second: &'a HashMap<K, V>,
55) -> impl Iterator<Item = (&'a K, Option<&'a V>, Option<&'a V>)> {
56 let keys_in_first = first.iter().map(move |(k, v)| (k, Some(v), second.get(k)));
57 let keys_only_in_second = second
58 .iter()
59 .filter(move |(k, _)| !first.contains_key(k))
60 .map(|(k, v)| (k, None, Some(v)));
61
62 keys_in_first.chain(keys_only_in_second)
63}
64
65#[cfg(test)]
66mod tests {
67 use crate::core::value::Value;
68 use crate::testing::has_unique_solution;
69 use crate::*;
70 use std::collections::HashMap;
71
72 #[test]
73 fn two_empty_hashmaps_are_unifiable() {
74 let a: HashMap<(), Value> = hashmap![];
75 let b: HashMap<(), Value> = hashmap![];
76 has_unique_solution(run!(q, eq(a, b)), Value::rv(0));
77 }
78}