th-desugar-1.2.0: Language/Haskell/TH/Desugar/Expand.hs
{- Language/Haskell/TH/Desugar/Expand.hs
(c) Richard Eisenberg 2013
eir@cis.upenn.edu
-}
{-# LANGUAGE CPP #-}
{-| Expands type synonyms in desugared types, ignoring type families.
See also the package th-expand-syns for doing this to non-desugared types.
-}
module Language.Haskell.TH.Desugar.Expand (
expand, expandType, substTy
) where
import qualified Data.Map as M
import Control.Applicative
import Language.Haskell.TH hiding (cxt)
import Language.Haskell.TH.Syntax ( Quasi(..) )
import Data.Data
import Data.Generics
import Language.Haskell.TH.Desugar.Core
import Language.Haskell.TH.Desugar.Util
-- | Expands all type synonyms in a desugared type.
expandType :: Quasi q => DType -> q DType
expandType = go []
where
go [] (DForallT tvbs cxt ty) =
DForallT tvbs <$> mapM expand cxt <*> expandType ty
go _ (DForallT {}) =
impossible "A forall type is applied to another type."
go args (DAppT t1 t2) = do
t2' <- expandType t2
go (t2' : args) t1
go args (DSigT ty ki) = do
ty' <- go [] ty
return $ foldl DAppT (DSigT ty' ki) args
go args (DConT n) = do
info <- reifyWithWarning n
case info of
TyConI (TySynD _n tvbs rhs)
| length args >= length tvbs -> do
let (syn_args, rest_args) = splitAtList tvbs args
rhs' <- dsType rhs
rhs'' <- expandType rhs'
tvbs' <- mapM dsTvb tvbs
ty <- substTy (M.fromList $ zip (map extractDTvbName tvbs') syn_args) rhs''
return $ foldl DAppT ty rest_args
_ -> return $ foldl DAppT (DConT n) args
go args ty = return $ foldl DAppT ty args
-- | Capture-avoiding substitution on types
substTy :: Quasi q => M.Map Name DType -> 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 <*> pure ki
substTy vars (DVarT n)
| Just ty <- M.lookup n vars
= return ty
| otherwise
= return $ DVarT n
substTy _ ty = return ty
substTyVarBndrs :: Quasi q => M.Map Name DType -> [DTyVarBndr]
-> (M.Map Name DType -> [DTyVarBndr] -> q DType)
-> q DType
substTyVarBndrs vars tvbs thing = do
new_names <- mapM (const (qNewName "local")) tvbs
let new_vars = M.fromList (zip (map extractDTvbName tvbs) (map DVarT new_names))
-- this is very inefficient. Oh well.
thing (M.union vars new_vars) (zipWith substTvb tvbs new_names)
substTvb :: DTyVarBndr -> Name -> DTyVarBndr
substTvb (DPlainTV _) n = DPlainTV n
substTvb (DKindedTV _ k) n = DKindedTV n k
-- | Extract the name from a @TyVarBndr@
extractDTvbName :: DTyVarBndr -> Name
extractDTvbName (DPlainTV n) = n
extractDTvbName (DKindedTV n _) = n
substPred :: Quasi q => M.Map Name DType -> DPred -> q DPred
substPred vars (DClassP name tys) =
DClassP name <$> mapM (substTy vars) tys
substPred vars (DEqualP t1 t2) =
DEqualP <$> substTy vars t1 <*> substTy vars t2
-- | Expand all type synonyms in the desugared abstract syntax tree provided.
-- Normally, the first parameter should have a type like 'DExp' or 'DLetDec'.
expand :: (Quasi q, Data a) => a -> q a
expand = everywhereM (mkM expandType)