{-# LANGUAGE UndecidableInstances, IncoherentInstances,
ExistentialQuantification, ScopedTypeVariables, EmptyDataDecls,
MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, CPP
#-}
#if MIN_VERSION_template_haskell(2,10,0)
#else
{-# LANGUAGE OverlappingInstances #-}
#endif
{-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}
module Generics.RepLib.Unify
where
import Generics.RepLib.R
import Generics.RepLib.R1
import Generics.RepLib.RepAux
import Generics.RepLib.PreludeReps()
import Control.Monad.State
#if MIN_VERSION_transformers(0,4,0)
import Control.Monad.Trans.Except(ExceptT(..),throwE,runExceptT)
#else
import Control.Monad.Error
#endif
data Proxy a
type UnifyError = String
#if MIN_VERSION_transformers(0,4,0)
type UM n a b = ExceptT UnifyError (State (UnificationState n a)) b
throwError :: UnifyError -> UM n a b
throwError = throwE
#else
type UM n a b = ErrorT UnifyError (State (UnificationState n a)) b
runExceptT :: ErrorT e m a -> m (Either e a)
runExceptT = runErrorT
#endif
data UnifySubD n a b = UnifySubD { unifyStepD :: Proxy (n, a) -> b -> b -> UM n a (),
substD:: n -> a -> b -> b,
occursCheckD :: n -> Proxy a -> b -> Bool}
instance (Unify n a b, Subst n a b, Occurs n a b) => Sat (UnifySubD n a b) where
dict = UnifySubD {unifyStepD = unifyStep, substD = subst, occursCheckD = occursCheck}
data UConstraint n a = forall b. UC (UnifySubD n a b) b b
data UnificationState n a = UState {uConstraints :: [UConstraint n a],
uSubst :: [(n, a)]}
class (Eq n, Show n, Show a, Show b, HasVar n a) => Unify n a b where
unifyStep :: Proxy (n, a) -> b -> b -> UM n a ()
instance (Eq n, Show n, Show a, Show b, HasVar n a, Rep1 (UnifySubD n a) b) => Unify n a b where
unifyStep = unifyStepR1 rep1
unifyStepR1 :: (Eq n, Show n, Show a, Show b, HasVar n a) => R1 (UnifySubD n a) b -> Proxy (n, a) -> b -> b -> UM n a ()
unifyStepR1 Int1 _ = unifyStepEq
unifyStepR1 Char1 _ = unifyStepEq
unifyStepR1 Integer1 _ = unifyStepEq
unifyStepR1 Float1 _ = unifyStepEq
unifyStepR1 Double1 _ = unifyStepEq
unifyStepR1 (Data1 _ cons) dum =
\ x y ->
let loop (Con rcd rec : rest) =
case (from rcd x, from rcd y) of
(Just p1, Just p2) -> addConstraintsRL1 rec dum p1 p2
(Nothing, Nothing) -> loop rest
(_,_) -> throwError ("constructor mismatch when trying to match " ++ show x ++ " = " ++ show y)
in loop cons
unifyStepR1 _ _ = \_ _ -> throwError ("unifyStepR1 unhandled generic type constructor")
addConstraintsRL1 :: MTup (UnifySubD n a) l -> Proxy (n, a) -> l -> l -> UM n a ()
addConstraintsRL1 MNil _ Nil Nil = return ()
addConstraintsRL1 (r :+: rl) (dum :: Proxy (n, a)) (p1 :*: t1) (p2 :*: t2) =
do queueConstraint $ UC r p1 p2
addConstraintsRL1 rl dum t1 t2
unifyStepEq :: (Eq b, Show b) => b -> b -> UM n a ()
unifyStepEq x y = if x == y
then return ()
else throwError $ "unify failed when testing equality for " ++ show x ++ " = " ++ show y
instance (Eq n, Show n, Show a, HasVar n a, Rep1 (UnifySubD n a) a) => Unify n a a where
unifyStep (dum :: Proxy (n, a)) (a1::a) a2 =
case ((is_var a1) :: Maybe n, (is_var a2) :: Maybe n) of
(Just n1, Just n2) -> if n1 == n2
then return ()
else addSub n1 ((var n2) :: a);
(Just n1, _) -> addSub n1 a2
(_, Just n2) -> addSub n2 a1
(_, _) -> unifyStepR1 rep1 dum a1 a2
where
addSub n t = extendSubstitution (n, t)
dequeueConstraint :: UM n a (Maybe (UConstraint n a))
dequeueConstraint = do s <- get
case s of (UState [] _) -> return Nothing
(UState (x : xs) sub) -> do put $ UState xs sub
return $ Just x
queueConstraint :: UConstraint n a -> UM n a ()
queueConstraint eq = modify (\ (UState xs sub) -> (UState (eq : xs) sub))
extendSubstitution :: (HasVar n a, Eq n, Show n, Show a, Rep1 (UnifySubD n a) a) => (n, a) -> UM n a ()
extendSubstitution asgn@((n :: n), (a :: a)) =
if (occursCheck n (undefined :: Proxy a) a)
then throwError $ "occurs check failed when extending sub with " ++ (show n) ++ " = " ++ (show a)
else do (UState xs sub) <- get
let sub' = [(n', subst n a a') | (n', a') <- sub]
let xs' = [UC d (substD d n a b1) (substD d n a b2) | (UC d b1 b2) <- xs]
put (UState xs' (asgn : sub'))
solveUnification :: (HasVar n a, Eq n, Show n, Show a, Rep1 (UnifySubD n a) a) => [(a, a)] -> Maybe [(n, a)]
solveUnification (eqs :: [(a, a)]) =
case r of Left e -> error e
Right _ -> Just $ uSubst final
where
(r, final) = runState (runExceptT rwConstraints) (UState cs [])
cs = [(UC dict a1 a2) | (a1, a2) <- eqs]
rwConstraints :: UM n a ()
rwConstraints = do c <- dequeueConstraint
case c of Just (UC d a1 a2) -> do _ <- unifyStepD d (undefined :: Proxy (n, a)) a1 a2
rwConstraints
Nothing -> return ()
solveUnification' :: (HasVar n a, Eq n, Show n, Show a, Show b, Rep1 (UnifySubD n a) b) => Proxy (n, a) -> [(b, b)] -> Maybe [(n, a)]
solveUnification' (dum :: Proxy (n, a)) (eqs :: [(b, b)]) =
case r of Left e -> error e
Right _ -> Just $ uSubst final
where
(r, final) = runState (runExceptT rwConstraints) (UState cs [])
cs = [(UC dict a1 a2) | (a1, a2) <- eqs]
rwConstraints :: UM n a ()
rwConstraints = do c <- dequeueConstraint
case c of Just (UC d a1 a2) -> do _ <- unifyStepD d dum a1 a2
rwConstraints
Nothing -> return ()
class HasVar a b where
is_var :: b -> Maybe a
var :: a -> b
class Subst a t t' where
subst :: a -> t -> t' -> t'
instance Rep1 (UnifySubD a t) t' => Subst a t t' where
subst = substR1 rep1
substR1 :: Rep1 (UnifySubD a t) t' => R1 (UnifySubD a t) t' -> a -> t -> t' -> t'
substR1 _ (a::a) (t::t) t' = gmapT1 (\cb b -> substD cb a t b) t'
instance (Eq a, HasVar a t, Rep1 (UnifySubD a t) t) => Subst a t t where
subst a t t' = if is_var t' == Just a
then t
else gmapT1 (\cb b -> substD cb a t b) t'
class Occurs n a b where
occursCheck :: n -> Proxy a -> b -> Bool
instance Rep1 (UnifySubD n a) b => Occurs n a b where
occursCheck = occursCheckR1 rep1
occursCheckR1 :: Rep1 (UnifySubD n a) b => R1 (UnifySubD n a) b -> n -> Proxy a -> b -> Bool
occursCheckR1 _ (n::n) pa b = or $ gmapQ1 (\cb b' -> occursCheckD cb n pa b') b
instance (Eq n, HasVar n a, Rep1 (UnifySubD n a) a) => Occurs n a a where
occursCheck n pa a = if is_var a == Just n
then True
else or $ gmapQ1 (\cb b -> occursCheckD cb n pa b) a