HaRe-0.6: tools/base/TI/Unification.hs
{-+
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