Crate hashconsing [−] [src]
Hash consing library.
Straightforward implementation of the paper by Filiâtre and Conchon.
Provides constant time comparison and perfect (maximal) sharing assuming only
one HashConsign is created for a given type. This assumption must never
be falsified unless you really know what you are doing.
Hash consed elements are immutable and therefore thread-safe: HConsed
implements Send and Sync.
TODO
Ideally we'd like to use weak references in the consign but they're currently feature-gated. Waiting for dust to settle.
Example
Simple example for lambda calculus from the paper.
extern crate hashconsing ; use std::fmt ; use ::hashconsing::* ; use self::ActualTerm::* ; type Term = HConsed<ActualTerm> ; #[derive(Hash)] enum ActualTerm { Var(usize), Lam(Term), App(Term, Term) } impl PartialEq for ActualTerm { fn eq(& self, rhs: & Self) -> bool { match (self, rhs) { (& Var(i), & Var(j)) => i == j, (& Lam(ref t1), & Lam(ref t2)) => t1.hkey() == t2.hkey(), (& App(ref u1, ref v1), & App(ref u2, ref v2)) => u1.hkey() == u2.hkey() && v1.hkey() == v2.hkey(), _ => false } } } impl Eq for ActualTerm {} impl fmt::Display for ActualTerm { fn fmt(& self, fmt: & mut fmt::Formatter) -> fmt::Result { match self { & Var(i) => write!(fmt, "v{}", i), & Lam(ref t) => write!(fmt, "({})", t.get()), & App(ref u, ref v) => write!(fmt, "{}.{}", u.get(), v.get()), } } } trait TermFactory { fn var(& mut self, v: usize) -> Term ; fn lam(& mut self, t: Term) -> Term ; fn app(& mut self, u: Term, v: Term) -> Term ; } impl TermFactory for HashConsign<ActualTerm> { fn var(& mut self, v: usize) -> Term { self.mk( Var(v) ) } fn lam(& mut self, t: Term) -> Term { self.mk( Lam(t) ) } fn app(& mut self, u: Term, v: Term) -> Term { self.mk( App(u, v) ) } } pub fn main() { let mut consign = HashConsign::empty() ; assert_eq!(consign.len(), 0) ; let v = consign.var(0) ; assert_eq!(consign.len(), 1) ; let v2 = consign.var(3) ; assert_eq!(consign.len(), 2) ; let lam = consign.lam(v2) ; assert_eq!(consign.len(), 3) ; let v3 = consign.var(3) ; assert_eq!(consign.len(), 3) ; let lam2 = consign.lam(v3) ; assert_eq!(consign.len(), 3) ; let app = consign.app(lam2, v) ; assert_eq!(consign.len(), 4) ; }
Concurrent version
extern crate hashconsing ; use std::thread ; use std::sync::{ Arc, mpsc, Mutex } ; use std::fmt ; use hashconsing::* ; use self::ActualTerm::* ; type Term = HConsed<ActualTerm> ; #[derive(Hash)] enum ActualTerm { Var(usize), Lam(Term), App(Term, Term) } impl PartialEq for ActualTerm { fn eq(& self, rhs: & Self) -> bool { match (self, rhs) { (& Var(i), & Var(j)) => i == j, (& Lam(ref t1), & Lam(ref t2)) => t1.hkey() == t2.hkey(), (& App(ref u1, ref v1), & App(ref u2, ref v2)) => u1.hkey() == u2.hkey() && v1.hkey() == v2.hkey(), _ => false } } } impl Eq for ActualTerm {} impl fmt::Display for ActualTerm { fn fmt(& self, fmt: & mut fmt::Formatter) -> fmt::Result { match self { & Var(i) => write!(fmt, "v{}", i), & Lam(ref t) => write!(fmt, "({})", t.get()), & App(ref u, ref v) => write!(fmt, "{}.{}", u.get(), v.get()), } } } trait TermFactory { fn var(& self, v: usize) -> Term ; fn lam(& self, t: Term) -> Term ; fn app(& self, u: Term, v: Term) -> Term ; fn len(& self) -> usize ; } impl TermFactory for Arc<Mutex<HashConsign<ActualTerm>>> { fn var(& self, v: usize) -> Term { self.lock().unwrap().mk( Var(v) ) } fn lam(& self, t: Term) -> Term { self.lock().unwrap().mk( Lam(t) ) } fn app(& self, u: Term, v: Term) -> Term { self.lock().unwrap().mk( App(u, v) ) } fn len(& self) -> usize { self.lock().unwrap().len() } } pub fn main() { let thread_count = 4 ; let consign = Arc::new( Mutex::new(HashConsign::empty()) ) ; assert_eq!(consign.len(), 0) ; // Master to slaves channel. let (tx, rx) = mpsc::channel() ; // Slave to slave channel. let (ts, rs) = mpsc::channel() ; let mut bla = consign.var(0) ; for i in 1..(thread_count + 1) { let (consign, tx) = (consign.clone(), tx.clone()) ; let ts = ts.clone() ; thread::spawn(move || { let v = consign.var(0) ; let v2 = consign.var(3) ; let lam = consign.lam(v2) ; if i == 1 { ts.send(lam.clone()).unwrap() ; () } ; let v3 = consign.var(3) ; if i == 2 { ts.send(v3.clone()).unwrap() ; () } ; let lam2 = consign.lam(v3) ; if i == 3 { ts.send(lam2.clone()).unwrap() ; () } ; let app = consign.app(lam2, v) ; if i == 4 { ts.send(app).unwrap() ; () } ; tx.send(i) }) ; } for _ in 0..thread_count { match rs.recv() { Ok(t) => bla = consign.app(t, bla), Err(e) => panic!("error {}.", e), } match rx.recv() { Ok(i) => println!("| Thread {} is done.", i), Err(e) => panic!("error {}.", e), } } assert_eq!(consign.len(), 7) ; }
Structs
| HConsed |
Stores a hash consed element and its hash in order to avoid recomputing it every time. |
| HashConsign |
The consign storing the actual hash consed elements as |