{-+ Generic unification and matching -} module Unification where import Maybe(fromMaybe) import List(nub) import MUtils(mapFst,mapSnd,mapBoth,apBoth) {-+ Term types to be unified (or matched) must be instances of the following class: -} class (Show term,Eq var) => Unifiable term var | term -> var where topMatch :: Equation term -> Maybe (Equations term) isVar :: term -> Maybe var subst :: Substitution var term -> term -> term showTerm :: term -> String -- for error reporting with fail showTerm = show type Equation t = (t,t) type Equations t = [Equation t] type Substitution v t = [(v,t)] --mapSubst = mapSnd mapSubst f s = [seq y (v,y)|(v,x)<-s,let y=f x]-- more strict to avoid space leaks mapEqns = mapBoth unify :: (Monad m,Unifiable t v) => Equations t -> m (Substitution v t) unify = uni [] where -- Invariant: the vars of eqns are not in the domain of s -- s is idempotent uni s [] = return s uni s (eqn@(t1,t2):eqns) = case topMatch eqn of Just eqns' -> uni s (eqns'++eqns) _ -> case apBoth isVar eqn of (Just v,_) -> varBind v t2 (_,Just v) -> varBind v t1 _ -> mismatch' "(unify)" t1 t2 where -- Pre: t is not the variable v. (This is ensured by topMatch.) varBind v t = if occurs v t then mismatch' "(occurs check)" t1 t2 else let s1=(v,t) su=subst [s1] in uni (s1:mapSubst su s) (mapEqns su eqns) match :: (Monad m,Unifiable t v) => Equations t -> m (Substitution v t) -- Pre: lhs vars are disjoint from rhs vars match eqns = uni [] eqns where pvars = nub $ concatMap (vars.snd) eqns -- Invariant: the vars of the lhss of the eqns are not in the domain of s uni s [] = return s uni s (eqn@(t1,t2):eqns) = case topMatch eqn of Just eqns' -> uni s (eqns'++eqns) _ -> case isVar t1 of Just v -> varBind v t2 _ -> mismatch' "(match)" t1 t2 where -- Pre: t is not the variable v. (This is ensured by topMatch.) varBind v t = if v `elem` pvars then mismatch' "(matchvar)" t1 t2 else let s1=(v,t) su=subst [s1] in uni (s1:mapSubst su s) (mapFst su eqns) mismatch t1 t2 = mismatch' "" t1 t2 mismatch' s t1 t2 = fail ("Mismatch: "++s++"\n "++showTerm t1++"\nvs\n "++showTerm t2) subterms t = map fst $ fromMaybe bug (topMatch (t,t)) -- clumpsy... where bug = error $ "Unification: bug in topMatch: term doesn't match itself:"++showTerm t vars t = case isVar t of Just v -> [v] _ -> nub $ concatMap vars $ subterms t occurs v t = v `elem` vars t