ideas-1.0: src/Common/Rewriting/Unification.hs
-----------------------------------------------------------------------------
-- Copyright 2011, Open Universiteit Nederland. This file is distributed
-- under the terms of the GNU General Public License. For more information,
-- see the file "LICENSE.txt", which is included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer : bastiaan.heeren@ou.nl
-- Stability : provisional
-- Portability : portable (depends on ghc)
--
-----------------------------------------------------------------------------
module Common.Rewriting.Unification
( unify, match, matchA, specialLeft, specialRight
, unificationTests
) where
import Common.Rewriting.AC (pairingsA)
import Common.Rewriting.Substitution
import Common.Rewriting.Term
import Common.Utils.TestSuite
import Control.Arrow
import Control.Monad
-----------------------------------------------------------
-- Unification (in both ways)
unify :: Term -> Term -> Maybe Substitution
unify term1 term2 =
case (term1, term2) of
(TMeta i, TMeta j) | i == j ->
return emptySubst
(TMeta i, _) | not (i `hasMetaVar` term2) ->
return (singletonSubst i term2)
(_, TMeta j) | not (j `hasMetaVar` term1) ->
return (singletonSubst j term1)
(TApp f a, TApp g b) -> do
s1 <- unify f g
s2 <- unify (s1 |-> a) (s1 |-> b)
return (s2 @@ s1)
_ | term1 == term2 ->
return emptySubst
_ -> Nothing
match :: MonadPlus m => Term -> Term -> m Substitution
match term1 term2 =
case (term1, term2) of
(TMeta i, TMeta j) | i == j ->
return emptySubst
(TMeta i, _) | not (i `hasMetaVar` term2) ->
return (singletonSubst i term2)
(_, TMeta _) ->
fail "unifyM: no unifier"
(TApp f a, TApp g b) -> do
s1 <- match f g
s2 <- match (s1 |-> a) b
guard (composable s1 s2)
return (s1 @@ s2)
_ | term1 == term2 ->
return emptySubst
_ -> fail "unifyM: no unifier"
-----------------------------------------------------------
-- Matching (or: one-way unification)
-- second term should not have meta variables
matchA :: Term -> Term -> [Substitution]
matchA = rec True
where
rec _ (TMeta i) y =
return (singletonSubst i y)
rec isTop x y =
case getSpine x of
(TCon s, [a1, a2]) | isAssociative s ->
concatMap (uncurry recList . unzip) (associativeMatch isTop s a1 a2 y)
(a, as) -> do
let (b, bs) = getSpine y
guard (a == b)
recList as bs
recList [] [] = return emptySubst
recList (x:xs) (y:ys) = do
s1 <- rec False x y
s2 <- recList (map (s1 |->) xs) (map (s1 |->) ys)
return (s2 @@ s1)
recList _ _ = []
associativeMatch :: Bool -> Symbol -> Term -> Term -> Term -> [[(Term, Term)]]
associativeMatch isTop s1 a1 a2 (TApp (TApp (TCon s2) b1) b2)
| s1==s2 = map (map make) result
where
as = collect a1 . collect a2 $ []
bs = collect b1 . collect b2 $ []
list | isTop = map ($ as) [id, extLeft, extRight, extBoth]
| otherwise = [as]
extLeft = (TMeta specialLeft:)
extRight = (++[TMeta specialRight])
extBoth = extLeft . extRight
result = concatMap (\zs -> pairingsA True zs bs) list
make = construct *** construct
collect term =
case getFunction term of
Just (t, [a, b]) | s1==t -> collect a . collect b
_ -> (term:)
construct xs
| null xs = error "associativeMatcher: empty list"
| otherwise = foldr1 (binary s1) xs
associativeMatch _ _ _ _ _ = []
specialLeft, specialRight :: Int -- special meta variables for context extension
specialLeft = maxBound
specialRight = pred specialLeft
-----------------------------------------------------------
--- * Test unification properties
unificationTests :: TestSuite
unificationTests = suite "Unification" $ do
addProperty "unify" $ \a b ->
case unify a b of
Just s -> (s |-> a) == (s |-> b)
Nothing -> True
addProperty "unify-succeed" $ \a s ->
let b = s |-> a in
case unify a b of
Just s2 -> (s2 |-> a) == (s2 |-> b)
Nothing -> False
addProperty "match" $ \a b ->
case match a b of
Just s -> (s |-> a) == b
Nothing -> True
addProperty "match-succeed" $ \a s ->
let b = s |-> a in
case match a (s |-> a) of
Just s2 -> (s2 |-> a) == b
Nothing -> True