module Ideas.Common.Rewriting.Unification
( unify, match, matchExtended, matchList
, Match, SymbolMatch
, unificationTests
) where
import Control.Monad
import Data.Maybe
import Ideas.Common.Rewriting.AC (pairingsMatchA)
import Ideas.Common.Rewriting.Substitution
import Ideas.Common.Rewriting.Term
import Ideas.Common.Utils.TestSuite
import qualified Data.Map as M
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)
(TCon s xs, TCon t ys) | s == t ->
rec xs ys
(TList xs, TList ys) ->
rec xs ys
_ | term1 == term2 ->
return emptySubst
_ -> Nothing
where
rec [] [] = return emptySubst
rec (x:xs) (y:ys) = do
s1 <- unify x y
s2 <- rec (map (s1 |->) xs) (map (s1 |->) ys)
return (s2 @@ s1)
rec _ _ = fail "match: no unifier"
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 "match: no unifier"
(TCon s xs, TCon t ys) | s == t ->
rec xs ys
(TList xs, TList ys) ->
rec xs ys
_ | term1 == term2 ->
return emptySubst
_ -> fail "match: no unifier"
where
rec [] [] = return emptySubst
rec (x:xs) (y:ys) = do
s1 <- match x y
s2 <- rec (map (s1 |->) xs) ys
guard (composable s1 s2)
return (s1 @@ s2)
rec _ _ = fail "match: no unifier"
type Match a = a -> a -> [Substitution]
type SymbolMatch = Match Term -> [Term] -> Term -> [Substitution]
matchExtended :: M.Map Symbol SymbolMatch -> Term -> Term -> [(Substitution, Maybe Term, Maybe Term)]
matchExtended sm x y =
[ (sub, lookupVar mvLeft sub, lookupVar mvRight sub)
| f <- extensions
, sub <- matchA sm (f x) y
]
where
mvLeft = nextMetaVar x
mvRight = mvLeft + 1
extensions =
case x of
TCon s [_, _] | isAssociative s ->
let extLeft = binary s (TMeta mvLeft)
extRight = flip (binary s) (TMeta mvRight)
in [ f . g | f <- [id, extLeft], g <- [id, extRight] ]
_ -> [id]
matchA :: M.Map Symbol SymbolMatch -> Match Term
matchA sm = rec
where
rec (TMeta i) y =
return (singletonSubst i y)
rec (TList xs) (TList ys) =
matchList rec xs ys
rec x y =
case getFunction x of
Just (s, as) ->
case M.lookup s sm of
Just f -> f rec as y
Nothing
| isAssociative s -> associativeMatch s rec as y
| otherwise -> defaultMatch rec x y
_ -> defaultMatch rec x y
defaultMatch :: Match Term -> Match Term
defaultMatch f x y =
case (x, y) of
(TCon s xs, TCon t ys) -> do
guard (s == t)
matchList f xs ys
(TList xs, TList ys) ->
matchList f xs ys
_ -> do
guard (x == y)
return emptySubst
matchList :: Match Term -> Match [Term]
matchList f as bs =
case safeZipWith f as bs of
Just ms -> products ms
Nothing -> fail "matchList: lengths differ"
safeZipWith :: (a -> b -> c) -> [a] -> [b] -> Maybe [c]
safeZipWith f = rec
where
rec [] [] = Just []
rec (a:as) (b:bs) = liftM (f a b:) (rec as bs)
rec _ _ = Nothing
products :: [[Substitution]] -> [Substitution]
products = foldr op [emptySubst]
where
op xs ys = catMaybes [ x @+@ y | x <- xs, y <- ys ]
associativeMatch :: Symbol -> SymbolMatch
associativeMatch s f as b =
pairingsMatchA make (collects as []) (collect b []) >>= products
where
make :: Term -> [Term] -> [Substitution]
make (TMeta i) xs = [singletonSubst i (construct xs)]
make x [y] = f x y
make _ _ = []
collects = foldr ((.) . collect) id
collect term = maybe (term:) collects (isFunction s term)
construct xs
| null xs = error "associativeMatch: empty list"
| otherwise = foldr1 (binary s) xs
unificationTests :: TestSuite
unificationTests = suite "Unification"
[ useProperty "unify" $ \a b ->
case unify a b of
Just s -> (s |-> a) == (s |-> b)
Nothing -> True
, useProperty "unify-succeed" $ \a s ->
let b = s |-> a in
case unify a b of
Just s2 -> (s2 |-> a) == (s2 |-> b)
Nothing -> False
, useProperty "match" $ \a b ->
case match a b of
Just s -> (s |-> a) == b
Nothing -> True
, useProperty "match-succeed" $ \a s ->
let b = s |-> a in
case match a (s |-> a) of
Just s2 -> (s2 |-> a) == b
Nothing -> True
]