module Language.Haskell.TH.Desugar.Subst (
DSubst,
substTy, unionSubsts, unionMaybeSubsts,
IgnoreKinds(..), matchTy
) where
import qualified Data.Map as M
import qualified Data.Set as S
import Data.Generics
import Data.List
import Language.Haskell.TH.Desugar.Core
import Language.Haskell.TH.Syntax
import Language.Haskell.TH.Desugar.Util
#if __GLASGOW_HASKELL__ < 710
import Control.Applicative
#endif
type DSubst = M.Map Name DType
substTy :: Quasi q => DSubst -> DType -> q DType
substTy vars (DForallT tvbs cxt ty) =
substTyVarBndrs vars tvbs $ \vars' tvbs' -> do
cxt' <- mapM (substPred vars') cxt
ty' <- substTy vars' ty
return $ DForallT tvbs' cxt' ty'
substTy vars (DAppT t1 t2) =
DAppT <$> substTy vars t1 <*> substTy vars t2
substTy vars (DSigT ty ki) =
DSigT <$> substTy vars ty <*> substTy vars ki
substTy vars (DVarT n)
| Just ty <- M.lookup n vars
= return ty
| otherwise
= return $ DVarT n
substTy _ ty = return ty
substTyVarBndrs :: Quasi q => DSubst -> [DTyVarBndr]
-> (DSubst -> [DTyVarBndr] -> q DType)
-> q DType
substTyVarBndrs vars tvbs thing = do
(vars', tvbs') <- mapAccumLM substTvb vars tvbs
thing vars' tvbs'
substTvb :: Quasi q => DSubst -> DTyVarBndr
-> q (DSubst, DTyVarBndr)
substTvb vars (DPlainTV n) = do
new_n <- qNewName (nameBase n)
return (M.insert n (DVarT new_n) vars, DPlainTV new_n)
substTvb vars (DKindedTV n k) = do
new_n <- qNewName (nameBase n)
k' <- substTy vars k
return (M.insert n (DVarT new_n) vars, DKindedTV new_n k')
substPred :: Quasi q => DSubst -> DPred -> q DPred
substPred vars (DAppPr p t) = DAppPr <$> substPred vars p <*> substTy vars t
substPred vars (DSigPr p k) = DSigPr <$> substPred vars p <*> substTy vars k
substPred vars (DVarPr n)
| Just ty <- M.lookup n vars
= dTypeToDPred ty
| otherwise
= return $ DVarPr n
substPred _ p = return p
unionSubsts :: DSubst -> DSubst -> Maybe DSubst
unionSubsts a b =
let shared_key_set = M.keysSet a `S.intersection` M.keysSet b
matches_up = S.foldr (\name -> ((a M.! name) `geq` (b M.! name) &&))
True shared_key_set
in
if matches_up then return (a `M.union` b) else Nothing
data IgnoreKinds = YesIgnore | NoIgnore
matchTy :: IgnoreKinds -> DType -> DType -> Maybe DSubst
matchTy _ (DVarT var_name) arg = Just $ M.singleton var_name arg
matchTy ign (DSigT ty _ki) arg = case ign of
YesIgnore -> matchTy ign ty arg
NoIgnore -> Nothing
matchTy ign pat (DSigT ty _ki) = matchTy ign pat ty
matchTy _ (DForallT {}) _ =
error "Cannot match a forall in a pattern"
matchTy _ _ (DForallT {}) =
error "Cannot match a forall in a target"
matchTy ign (DAppT pat1 pat2) (DAppT arg1 arg2) =
unionMaybeSubsts [matchTy ign pat1 arg1, matchTy ign pat2 arg2]
matchTy _ (DConT pat_con) (DConT arg_con)
| pat_con == arg_con = Just M.empty
matchTy _ DArrowT DArrowT = Just M.empty
matchTy _ (DLitT pat_lit) (DLitT arg_lit)
| pat_lit == arg_lit = Just M.empty
matchTy _ _ _ = Nothing
unionMaybeSubsts :: [Maybe DSubst] -> Maybe DSubst
unionMaybeSubsts = foldl' union_subst1 (Just M.empty)
where
union_subst1 :: Maybe DSubst -> Maybe DSubst -> Maybe DSubst
union_subst1 ma mb = do
a <- ma
b <- mb
unionSubsts a b