--
-- Substitutions and Unification of Prolog Terms
-- Mark P. Jones November 1990
--
-- uses Haskell B. version 0.99.3
--
module Subst(Subst(..), nullSubst, (>!), (@@), apply, unify) where

import PrologData

infixr 3 @@
infix  4 >!

--- Substitutions:

type Subst = Id -> Term

-- substitutions are represented by functions mapping identifiers to terms.
--
-- apply s   extends the substitution s to a function mapping terms to terms
-- nullSubst is the empty substitution which maps every identifier to the
--           same identifier (as a term).
-- i >! t   is the substitution which maps the identifier i to the term t,
--           but otherwise behaves like nullSubst.
-- s1 @@ s2  is the composition of substitutions s1 and s2
--           N.B.  apply is a monoid homomorphism from (Subst,nullSubst,(@@))
--           to (Term -> Term, id, (.)) in the sense that:
--                  apply (s1 @@ s2) = apply s1 . apply s2
--                    s @@ nullSubst = s = nullSubst @@ s

apply                   :: Subst -> Term -> Term
apply s (Var i)          = s i
apply s (Struct a ts)    = Struct a (map (apply s) ts)

nullSubst               :: Subst
nullSubst i              = Var i

(>!)                  :: Id -> Term -> Subst
(>!) i t j | j==i       = t
            | otherwise  = Var j

(@@)                    :: Subst -> Subst -> Subst
s1 @@ s2                 = apply s1 . s2 

--- Unification:

-- unify t1 t2 returns a list containing a single substitution s which is
--             the most general unifier of terms t1 t2.  If no unifier
--             exists, the list returned is empty.

unify :: Term -> Term -> [Subst]
unify (Var x)       (Var y)       = if x==y then [nullSubst] else [x>!Var y]
unify (Var x)       t2            = [ x >! t2 | not (x `elem` varsIn t2) ]
unify t1            (Var y)       = [ y >! t1 | not (y `elem` varsIn t1) ]
unify (Struct a ts) (Struct b ss) = [ u | a==b, u<-listUnify ts ss ]

listUnify :: [Term] -> [Term] -> [Subst]
listUnify []     []     = [nullSubst]
listUnify []     (r:rs) = []
listUnify (t:ts) []     = []
listUnify (t:ts) (r:rs) = [ u2 @@ u1 | u1<-unify t r,
                                       u2<-listUnify (map (apply u1) ts)
                                                     (map (apply u1) rs) ]

--- End of Subst.hs
