{-# Language CPP, DeriveDataTypeable #-}
#if MIN_VERSION_base(4,4,0)
#define HAS_GENERICS
{-# Language DeriveGeneric #-}
#endif
module Language.Haskell.TH.Datatype
(
DatatypeInfo(..)
, ConstructorInfo(..)
, DatatypeVariant(..)
, ConstructorVariant(..)
, FieldStrictness(..)
, Unpackedness(..)
, Strictness(..)
, reifyDatatype
, reifyConstructor
, reifyRecord
, normalizeInfo
, normalizeDec
, normalizeCon
, lookupByConstructorName
, lookupByRecordName
, TypeSubstitution(..)
, quantifyType
, freeVariablesWellScoped
, freshenFreeVariables
, equalPred
, classPred
, asEqualPred
, asClassPred
, dataDCompat
, newtypeDCompat
, tySynInstDCompat
, pragLineDCompat
, arrowKCompat
, isStrictAnnot
, notStrictAnnot
, unpackedAnnot
, resolveTypeSynonyms
, resolveKindSynonyms
, resolvePredSynonyms
, resolveInfixT
, reifyFixityCompat
, showFixity
, showFixityDirection
, unifyTypes
, tvName
, tvKind
, datatypeType
) where
import Data.Data (Typeable, Data)
import Data.Foldable (foldMap, foldl')
import Data.List (nub, find, union, (\\))
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe
import qualified Data.Set as Set
import Data.Set (Set)
import qualified Data.Traversable as T
import Control.Monad
import Language.Haskell.TH
#if MIN_VERSION_template_haskell(2,11,0)
hiding (Extension(..))
#endif
import Language.Haskell.TH.Datatype.Internal
import Language.Haskell.TH.Lib (arrowK, starK)
#ifdef HAS_GENERICS
import GHC.Generics (Generic)
#endif
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative (Applicative(..), (<$>))
import Data.Monoid (Monoid(..))
#endif
data DatatypeInfo = DatatypeInfo
{ datatypeContext :: Cxt
, datatypeName :: Name
, datatypeVars :: [Type]
, datatypeVariant :: DatatypeVariant
, datatypeCons :: [ConstructorInfo]
}
deriving (Show, Eq, Typeable, Data
#ifdef HAS_GENERICS
,Generic
#endif
)
data DatatypeVariant
= Datatype
| Newtype
| DataInstance
| NewtypeInstance
deriving (Show, Read, Eq, Ord, Typeable, Data
#ifdef HAS_GENERICS
,Generic
#endif
)
data ConstructorInfo = ConstructorInfo
{ constructorName :: Name
, constructorVars :: [TyVarBndr]
, constructorContext :: Cxt
, constructorFields :: [Type]
, constructorStrictness :: [FieldStrictness]
, constructorVariant :: ConstructorVariant
}
deriving (Show, Eq, Typeable, Data
#ifdef HAS_GENERICS
,Generic
#endif
)
data ConstructorVariant
= NormalConstructor
| InfixConstructor
| RecordConstructor [Name]
deriving (Show, Eq, Ord, Typeable, Data
#ifdef HAS_GENERICS
,Generic
#endif
)
data FieldStrictness = FieldStrictness
{ fieldUnpackedness :: Unpackedness
, fieldStrictness :: Strictness
}
deriving (Show, Eq, Ord, Typeable, Data
#ifdef HAS_GENERICS
,Generic
#endif
)
data Unpackedness
= UnspecifiedUnpackedness
| NoUnpack
| Unpack
deriving (Show, Eq, Ord, Typeable, Data
#ifdef HAS_GENERICS
,Generic
#endif
)
data Strictness
= UnspecifiedStrictness
| Lazy
| Strict
deriving (Show, Eq, Ord, Typeable, Data
#ifdef HAS_GENERICS
,Generic
#endif
)
isStrictAnnot, notStrictAnnot, unpackedAnnot :: FieldStrictness
isStrictAnnot = FieldStrictness UnspecifiedUnpackedness Strict
notStrictAnnot = FieldStrictness UnspecifiedUnpackedness UnspecifiedStrictness
unpackedAnnot = FieldStrictness Unpack Strict
datatypeType :: DatatypeInfo -> Type
datatypeType di
= foldl AppT (ConT (datatypeName di))
$ map stripSigT
$ datatypeVars di
reifyDatatype ::
Name ->
Q DatatypeInfo
reifyDatatype n = normalizeInfo' "reifyDatatype" isReified =<< reify n
reifyConstructor ::
Name ->
Q ConstructorInfo
reifyConstructor conName = do
dataInfo <- reifyDatatype conName
return $ lookupByConstructorName conName dataInfo
reifyRecord ::
Name ->
Q ConstructorInfo
reifyRecord recordName = do
dataInfo <- reifyDatatype recordName
return $ lookupByRecordName recordName dataInfo
lookupByConstructorName ::
Name ->
DatatypeInfo ->
ConstructorInfo
lookupByConstructorName conName dataInfo =
case find ((== conName) . constructorName) (datatypeCons dataInfo) of
Just conInfo -> conInfo
Nothing -> error $ "Datatype " ++ nameBase (datatypeName dataInfo)
++ " does not have a constructor named " ++ nameBase conName
lookupByRecordName ::
Name ->
DatatypeInfo ->
ConstructorInfo
lookupByRecordName recordName dataInfo =
case find (conHasRecord recordName) (datatypeCons dataInfo) of
Just conInfo -> conInfo
Nothing -> error $ "Datatype " ++ nameBase (datatypeName dataInfo)
++ " does not have any constructors with a "
++ "record selector named " ++ nameBase recordName
normalizeInfo :: Info -> Q DatatypeInfo
normalizeInfo = normalizeInfo' "normalizeInfo" isn'tReified
normalizeInfo' :: String -> IsReifiedDec -> Info -> Q DatatypeInfo
normalizeInfo' entry reifiedDec i =
case i of
PrimTyConI{} -> bad "Primitive type not supported"
ClassI{} -> bad "Class not supported"
#if MIN_VERSION_template_haskell(2,11,0)
FamilyI DataFamilyD{} _ ->
#elif MIN_VERSION_template_haskell(2,7,0)
FamilyI (FamilyD DataFam _ _ _) _ ->
#else
TyConI (FamilyD DataFam _ _ _) ->
#endif
bad "Use a value constructor to reify a data family instance"
#if MIN_VERSION_template_haskell(2,7,0)
FamilyI _ _ -> bad "Type families not supported"
#endif
TyConI dec -> normalizeDecFor reifiedDec dec
#if MIN_VERSION_template_haskell(2,11,0)
DataConI name _ parent -> reifyParent name parent
#else
DataConI name _ parent _ -> reifyParent name parent
#endif
#if MIN_VERSION_template_haskell(2,11,0)
VarI recName recTy _ -> reifyRecordType recName recTy
#else
VarI recName recTy _ _ -> reifyRecordType recName recTy
#endif
_ -> bad "Expected a type constructor"
where
bad msg = fail (entry ++ ": " ++ msg)
reifyParent :: Name -> Name -> Q DatatypeInfo
reifyParent con = reifyParentWith "reifyParent" p
where
p :: DatatypeInfo -> Bool
p info = con `elem` map constructorName (datatypeCons info)
reifyRecordType :: Name -> Type -> Q DatatypeInfo
reifyRecordType recName recTy =
let (_, argTys :|- _) = uncurryType recTy
in case argTys of
dataTy:_ -> decomposeDataType dataTy
_ -> notRecSelFailure
where
decomposeDataType :: Type -> Q DatatypeInfo
decomposeDataType ty =
do case decomposeType ty of
ConT parent :| _ -> reifyParentWith "reifyRecordType" p parent
_ -> notRecSelFailure
notRecSelFailure :: Q a
notRecSelFailure = fail $
"reifyRecordType: Not a record selector type: " ++
nameBase recName ++ " :: " ++ show recTy
p :: DatatypeInfo -> Bool
p info = any (conHasRecord recName) (datatypeCons info)
reifyParentWith ::
String ->
(DatatypeInfo -> Bool) ->
Name ->
Q DatatypeInfo
reifyParentWith prefix p n =
do info <- reify n
case info of
#if !(MIN_VERSION_template_haskell(2,11,0))
TyConI FamilyD{} -> dataFamiliesOnOldGHCsError
#endif
TyConI dec -> normalizeDecFor isReified dec
#if MIN_VERSION_template_haskell(2,7,0)
FamilyI dec instances ->
do let instances1 = map (repairDataFam dec) instances
instances2 <- mapM (normalizeDecFor isReified) instances1
case find p instances2 of
Just inst -> return inst
Nothing -> panic "lost the instance"
#endif
_ -> panic "unexpected parent"
where
dataFamiliesOnOldGHCsError :: Q a
dataFamiliesOnOldGHCsError = fail $
prefix ++ ": Data family instances can only be reified with GHC 7.4 or later"
panic :: String -> Q a
panic message = fail $ "PANIC: " ++ prefix ++ " " ++ message
#if MIN_VERSION_template_haskell(2,8,0) && (!MIN_VERSION_template_haskell(2,10,0))
sanitizeStars :: Kind -> Kind
sanitizeStars = go
where
go :: Kind -> Kind
go (AppT t1 t2) = AppT (go t1) (go t2)
go (SigT t k) = SigT (go t) (go k)
go (ConT n) | n == starKindName = StarT
go t = t
repairVarKindsWith' :: [TyVarBndr] -> [Type] -> [Type]
repairVarKindsWith' dvars ts =
let kindVars = freeVariables . map kindPart
kindPart (KindedTV _ k) = [k]
kindPart (PlainTV _ ) = []
nparams = length dvars
kparams = kindVars dvars
(tsKinds,tsNoKinds) = splitAt (length kparams) ts
tsKinds' = map sanitizeStars tsKinds
extraTys = drop (length tsNoKinds) (bndrParams dvars)
ts' = tsNoKinds ++ extraTys
in applySubstitution (Map.fromList (zip kparams tsKinds')) $
repairVarKindsWith dvars ts'
repairDataFam ::
Dec ->
Dec ->
Dec
repairDataFam
(FamilyD _ _ dvars _)
(NewtypeInstD cx n ts con deriv) =
NewtypeInstD cx n (repairVarKindsWith' dvars ts) con deriv
repairDataFam
(FamilyD _ _ dvars _)
(DataInstD cx n ts cons deriv) =
DataInstD cx n (repairVarKindsWith' dvars ts) cons deriv
#else
repairDataFam famD instD
# if MIN_VERSION_template_haskell(2,11,0)
| DataFamilyD _ dvars _ <- famD
, NewtypeInstD cx n ts k c deriv <- instD
= NewtypeInstD cx n (repairVarKindsWith dvars ts) k c deriv
| DataFamilyD _ dvars _ <- famD
, DataInstD cx n ts k c deriv <- instD
= DataInstD cx n (repairVarKindsWith dvars ts) k c deriv
# else
| FamilyD _ _ dvars _ <- famD
, NewtypeInstD cx n ts c deriv <- instD
= NewtypeInstD cx n (repairVarKindsWith dvars ts) c deriv
| FamilyD _ _ dvars _ <- famD
, DataInstD cx n ts c deriv <- instD
= DataInstD cx n (repairVarKindsWith dvars ts) c deriv
# endif
#endif
repairDataFam _ instD = instD
repairVarKindsWith :: [TyVarBndr] -> [Type] -> [Type]
repairVarKindsWith = zipWith stealKindForType
stealKindForType :: TyVarBndr -> Type -> Type
stealKindForType tvb t@VarT{} = SigT t (tvKind tvb)
stealKindForType _ t = t
normalizeDec :: Dec -> Q DatatypeInfo
normalizeDec = normalizeDecFor isn'tReified
normalizeDecFor :: IsReifiedDec -> Dec -> Q DatatypeInfo
normalizeDecFor isReified dec =
case dec of
#if MIN_VERSION_template_haskell(2,12,0)
NewtypeD context name tyvars _kind con _derives ->
giveTypesStarKinds <$> normalizeDec' isReified context name (bndrParams tyvars) [con] Newtype
DataD context name tyvars _kind cons _derives ->
giveTypesStarKinds <$> normalizeDec' isReified context name (bndrParams tyvars) cons Datatype
NewtypeInstD context name params _kind con _derives ->
repair13618' . giveTypesStarKinds =<<
normalizeDec' isReified context name params [con] NewtypeInstance
DataInstD context name params _kind cons _derives ->
repair13618' . giveTypesStarKinds =<<
normalizeDec' isReified context name params cons DataInstance
#elif MIN_VERSION_template_haskell(2,11,0)
NewtypeD context name tyvars _kind con _derives ->
giveTypesStarKinds <$> normalizeDec' isReified context name (bndrParams tyvars) [con] Newtype
DataD context name tyvars _kind cons _derives ->
giveTypesStarKinds <$> normalizeDec' isReified context name (bndrParams tyvars) cons Datatype
NewtypeInstD context name params _kind con _derives ->
repair13618' . giveTypesStarKinds =<<
normalizeDec' isReified context name params [con] NewtypeInstance
DataInstD context name params _kind cons _derives ->
repair13618' . giveTypesStarKinds =<<
normalizeDec' isReified context name params cons DataInstance
#else
NewtypeD context name tyvars con _derives ->
giveTypesStarKinds <$> normalizeDec' isReified context name (bndrParams tyvars) [con] Newtype
DataD context name tyvars cons _derives ->
giveTypesStarKinds <$> normalizeDec' isReified context name (bndrParams tyvars) cons Datatype
NewtypeInstD context name params con _derives ->
repair13618' . giveTypesStarKinds =<<
normalizeDec' isReified context name params [con] NewtypeInstance
DataInstD context name params cons _derives ->
repair13618' . giveTypesStarKinds =<<
normalizeDec' isReified context name params cons DataInstance
#endif
_ -> fail "normalizeDecFor: DataD or NewtypeD required"
where
repair13618' | isReified = repair13618
| otherwise = return
bndrParams :: [TyVarBndr] -> [Type]
bndrParams = map $ \bndr ->
case bndr of
KindedTV t k -> SigT (VarT t) k
PlainTV t -> VarT t
tvKind :: TyVarBndr -> Kind
tvKind (PlainTV _) = starK
tvKind (KindedTV _ k) = k
stripSigT :: Type -> Type
stripSigT (SigT t _) = t
stripSigT t = t
normalizeDec' ::
IsReifiedDec ->
Cxt ->
Name ->
[Type] ->
[Con] ->
DatatypeVariant ->
Q DatatypeInfo
normalizeDec' reifiedDec context name params cons variant =
do cons' <- concat <$> mapM (normalizeConFor reifiedDec name params variant) cons
return DatatypeInfo
{ datatypeContext = context
, datatypeName = name
, datatypeVars = params
, datatypeCons = cons'
, datatypeVariant = variant
}
normalizeCon ::
Name ->
[Type] ->
DatatypeVariant ->
Con ->
Q [ConstructorInfo]
normalizeCon = normalizeConFor isn'tReified
normalizeConFor ::
IsReifiedDec ->
Name ->
[Type] ->
DatatypeVariant ->
Con ->
Q [ConstructorInfo]
normalizeConFor reifiedDec typename params variant = fmap (map giveTyVarBndrsStarKinds) . dispatch
where
checkGadtFixity :: [Type] -> Name -> Q ConstructorVariant
checkGadtFixity ts n = do
#if MIN_VERSION_template_haskell(2,11,0)
mbFi <- return Nothing `recover` reifyFixity n
let userSuppliedFixity = isJust mbFi
#else
mbFi <- reifyFixityCompat n
let userSuppliedFixity = isJust mbFi && mbFi /= Just defaultFixity
#endif
return $ if isInfixDataCon (nameBase n)
&& length ts == 2
&& userSuppliedFixity
then InfixConstructor
else NormalConstructor
isInfixDataCon :: String -> Bool
isInfixDataCon (':':_) = True
isInfixDataCon _ = False
dispatch :: Con -> Q [ConstructorInfo]
dispatch =
let defaultCase :: Con -> Q [ConstructorInfo]
defaultCase = go [] [] False
where
go :: [TyVarBndr]
-> Cxt
-> Bool
-> Con
-> Q [ConstructorInfo]
go tyvars context gadt c =
case c of
NormalC n xs -> do
let (bangs, ts) = unzip xs
stricts = map normalizeStrictness bangs
fi <- if gadt
then checkGadtFixity ts n
else return NormalConstructor
return [ConstructorInfo n tyvars context ts stricts fi]
InfixC l n r ->
let (bangs, ts) = unzip [l,r]
stricts = map normalizeStrictness bangs in
return [ConstructorInfo n tyvars context ts stricts
InfixConstructor]
RecC n xs ->
let fns = takeFieldNames xs
stricts = takeFieldStrictness xs in
return [ConstructorInfo n tyvars context
(takeFieldTypes xs) stricts (RecordConstructor fns)]
ForallC tyvars' context' c' ->
go (tyvars'++tyvars) (context'++context) True c'
#if MIN_VERSION_template_haskell(2,11,0)
GadtC ns xs innerType ->
let (bangs, ts) = unzip xs
stricts = map normalizeStrictness bangs in
gadtCase ns innerType ts stricts (checkGadtFixity ts)
RecGadtC ns xs innerType ->
let fns = takeFieldNames xs
stricts = takeFieldStrictness xs in
gadtCase ns innerType (takeFieldTypes xs) stricts
(const $ return $ RecordConstructor fns)
where
gadtCase = normalizeGadtC typename params tyvars context
#endif
#if MIN_VERSION_template_haskell(2,8,0) && (!MIN_VERSION_template_haskell(2,10,0))
dataFamCompatCase :: Con -> Q [ConstructorInfo]
dataFamCompatCase = go []
where
go tyvars c =
case c of
NormalC n xs ->
let stricts = map (normalizeStrictness . fst) xs in
dataFamCase' n tyvars stricts NormalConstructor
InfixC l n r ->
let stricts = map (normalizeStrictness . fst) [l,r] in
dataFamCase' n tyvars stricts InfixConstructor
RecC n xs ->
let stricts = takeFieldStrictness xs in
dataFamCase' n tyvars stricts
(RecordConstructor (takeFieldNames xs))
ForallC tyvars' context' c' ->
go (tyvars'++tyvars) c'
dataFamCase' :: Name -> [TyVarBndr] -> [FieldStrictness]
-> ConstructorVariant
-> Q [ConstructorInfo]
dataFamCase' n tyvars stricts variant = do
mbInfo <- reifyMaybe n
case mbInfo of
Just (DataConI _ ty _ _) -> do
let (context, argTys :|- returnTy) = uncurryType ty
returnTy' <- resolveTypeSynonyms returnTy
normalizeGadtC typename params tyvars context [n]
returnTy' argTys stricts (const $ return variant)
_ -> fail $ unlines
[ "normalizeCon: Cannot reify constructor " ++ nameBase n
, "You are likely calling normalizeDec on GHC 7.6 or 7.8 on a data family"
, "whose type variables have been eta-reduced due to GHC Trac #9692."
, "Unfortunately, without being able to reify the constructor's type,"
, "there is no way to recover the eta-reduced type variables in general."
, "A recommended workaround is to use reifyDatatype instead."
]
mightHaveBeenEtaReduced :: [Type] -> Bool
mightHaveBeenEtaReduced ts =
case unsnoc ts of
Nothing -> False
Just (initTs :|- lastT) ->
case varTName lastT of
Nothing -> False
Just n -> not (n `elem` freeVariables initTs)
unsnoc :: [a] -> Maybe (NonEmptySnoc a)
unsnoc [] = Nothing
unsnoc (x:xs) = case unsnoc xs of
Just (a :|- b) -> Just ((x:a) :|- b)
Nothing -> Just ([] :|- x)
varTName :: Type -> Maybe Name
varTName (SigT t _) = varTName t
varTName (VarT n) = Just n
varTName _ = Nothing
in case variant of
DataInstance
| reifiedDec, mightHaveBeenEtaReduced params
-> dataFamCompatCase
NewtypeInstance
| reifiedDec, mightHaveBeenEtaReduced params
-> dataFamCompatCase
_ -> defaultCase
#else
in defaultCase
#endif
#if MIN_VERSION_template_haskell(2,11,0)
normalizeStrictness :: Bang -> FieldStrictness
normalizeStrictness (Bang upk str) =
FieldStrictness (normalizeSourceUnpackedness upk)
(normalizeSourceStrictness str)
where
normalizeSourceUnpackedness :: SourceUnpackedness -> Unpackedness
normalizeSourceUnpackedness NoSourceUnpackedness = UnspecifiedUnpackedness
normalizeSourceUnpackedness SourceNoUnpack = NoUnpack
normalizeSourceUnpackedness SourceUnpack = Unpack
normalizeSourceStrictness :: SourceStrictness -> Strictness
normalizeSourceStrictness NoSourceStrictness = UnspecifiedStrictness
normalizeSourceStrictness SourceLazy = Lazy
normalizeSourceStrictness SourceStrict = Strict
#else
normalizeStrictness :: Strict -> FieldStrictness
normalizeStrictness IsStrict = isStrictAnnot
normalizeStrictness NotStrict = notStrictAnnot
# if MIN_VERSION_template_haskell(2,7,0)
normalizeStrictness Unpacked = unpackedAnnot
# endif
#endif
normalizeGadtC ::
Name ->
[Type] ->
[TyVarBndr] ->
Cxt ->
[Name] ->
Type ->
[Type] ->
[FieldStrictness] ->
(Name -> Q ConstructorVariant)
->
Q [ConstructorInfo]
normalizeGadtC typename params tyvars context names innerType
fields stricts getVariant =
do
let conBoundNames =
concatMap (\tvb -> tvName tvb:freeVariables (tvKind tvb)) tyvars
conSubst <- T.sequence $ Map.fromList [ (n, newName (nameBase n))
| n <- conBoundNames ]
let conSubst' = fmap VarT conSubst
renamedTyvars =
map (\tvb -> case tvb of
PlainTV n -> PlainTV (conSubst Map.! n)
KindedTV n k -> KindedTV (conSubst Map.! n)
(applySubstitution conSubst' k)) tyvars
renamedContext = applySubstitution conSubst' context
renamedInnerType = applySubstitution conSubst' innerType
renamedFields = applySubstitution conSubst' fields
innerType' <- resolveTypeSynonyms renamedInnerType
case decomposeType innerType' of
ConT innerTyCon :| ts | typename == innerTyCon ->
let (substName, context1) =
closeOverKinds (kindsOfFVsOfTvbs renamedTyvars)
(kindsOfFVsOfTypes params)
(mergeArguments params ts)
subst = VarT <$> substName
exTyvars = [ tv | tv <- renamedTyvars, Map.notMember (tvName tv) subst ]
exTyvars' = substTyVarBndrs subst exTyvars
context2 = applySubstitution subst (context1 ++ renamedContext)
fields' = applySubstitution subst renamedFields
in sequence [ ConstructorInfo name exTyvars' context2
fields' stricts <$> variantQ
| name <- names
, let variantQ = getVariant name
]
_ -> fail "normalizeGadtC: Expected type constructor application"
closeOverKinds :: Map Name Kind
-> Map Name Kind
-> (Map Name Name, Cxt)
-> (Map Name Name, Cxt)
closeOverKinds domainFVKinds rangeFVKinds = go
where
go :: (Map Name Name, Cxt) -> (Map Name Name, Cxt)
go (subst, context) =
let substList = Map.toList subst
(kindsInner, kindsOuter) =
unzip $
mapMaybe (\(d, r) -> do d' <- Map.lookup d domainFVKinds
r' <- Map.lookup r rangeFVKinds
return (d', r'))
substList
(kindSubst, kindContext) = mergeArgumentKinds kindsOuter kindsInner
(restSubst, restContext)
= if Map.null kindSubst
then (Map.empty, [])
else go (kindSubst, kindContext)
finalSubst = Map.unions [subst, kindSubst, restSubst]
finalContext = nub $ concat [context, kindContext, restContext]
in (finalSubst, finalContext)
kindsOfFVsOfTypes :: [Type] -> Map Name Kind
kindsOfFVsOfTypes = foldMap go
where
go :: Type -> Map Name Kind
go (ForallT {}) = error "`forall` type used in data family pattern"
go (AppT t1 t2) = go t1 `Map.union` go t2
go (SigT t k) =
let kSigs =
#if MIN_VERSION_template_haskell(2,8,0)
go k
#else
Map.empty
#endif
in case t of
VarT n -> Map.insert n k kSigs
_ -> go t `Map.union` kSigs
go _ = Map.empty
kindsOfFVsOfTvbs :: [TyVarBndr] -> Map Name Kind
kindsOfFVsOfTvbs = foldMap go
where
go :: TyVarBndr -> Map Name Kind
go (PlainTV n) = Map.singleton n starK
go (KindedTV n k) =
let kSigs =
#if MIN_VERSION_template_haskell(2,8,0)
kindsOfFVsOfTypes [k]
#else
Map.empty
#endif
in Map.insert n k kSigs
mergeArguments ::
[Type] ->
[Type] ->
(Map Name Name, Cxt)
mergeArguments ns ts = foldr aux (Map.empty, []) (zip ns ts)
where
aux (f `AppT` x, g `AppT` y) sc =
aux (x,y) (aux (f,g) sc)
aux (VarT n,p) (subst, context) =
case p of
VarT m | m == n -> (subst, context)
| Just n' <- Map.lookup m subst
, n == n' -> (subst, context)
| Map.notMember m subst -> (Map.insert m n subst, context)
_ -> (subst, equalPred (VarT n) p : context)
aux (SigT x _, y) sc = aux (x,y) sc
aux (x, SigT y _) sc = aux (x,y) sc
aux _ sc = sc
mergeArgumentKinds ::
[Kind] ->
[Kind] ->
(Map Name Name, Cxt)
#if MIN_VERSION_template_haskell(2,8,0)
mergeArgumentKinds = mergeArguments
#else
mergeArgumentKinds _ _ = (Map.empty, [])
#endif
resolveTypeSynonyms :: Type -> Q Type
resolveTypeSynonyms t =
let f :| xs = decomposeType t
notTypeSynCase :: Type -> Q Type
notTypeSynCase ty = foldl AppT ty <$> mapM resolveTypeSynonyms xs
expandCon :: Name
-> Type
-> Q Type
expandCon n ty = do
mbInfo <- reifyMaybe n
case mbInfo of
Just (TyConI (TySynD _ synvars def))
-> resolveTypeSynonyms $ expandSynonymRHS synvars xs def
_ -> notTypeSynCase ty
in case f of
ForallT tvbs ctxt body ->
ForallT `fmap` mapM resolve_tvb_syns tvbs
`ap` mapM resolvePredSynonyms ctxt
`ap` resolveTypeSynonyms body
SigT ty ki -> do
ty' <- resolveTypeSynonyms ty
ki' <- resolveKindSynonyms ki
notTypeSynCase $ SigT ty' ki'
ConT n -> expandCon n (ConT n)
#if MIN_VERSION_template_haskell(2,11,0)
InfixT t1 n t2 -> do
t1' <- resolveTypeSynonyms t1
t2' <- resolveTypeSynonyms t2
expandCon n (InfixT t1' n t2')
UInfixT t1 n t2 -> do
t1' <- resolveTypeSynonyms t1
t2' <- resolveTypeSynonyms t2
expandCon n (UInfixT t1' n t2')
#endif
_ -> notTypeSynCase f
resolveKindSynonyms :: Kind -> Q Kind
#if MIN_VERSION_template_haskell(2,8,0)
resolveKindSynonyms = resolveTypeSynonyms
#else
resolveKindSynonyms = return
#endif
resolve_tvb_syns :: TyVarBndr -> Q TyVarBndr
resolve_tvb_syns tvb@PlainTV{} = return tvb
resolve_tvb_syns (KindedTV n k) = KindedTV n <$> resolveKindSynonyms k
expandSynonymRHS ::
[TyVarBndr] ->
[Type] ->
Type ->
Type
expandSynonymRHS synvars ts def =
let argNames = map tvName synvars
(args,rest) = splitAt (length argNames) ts
subst = Map.fromList (zip argNames args)
in foldl AppT (applySubstitution subst def) rest
resolvePredSynonyms :: Pred -> Q Pred
#if MIN_VERSION_template_haskell(2,10,0)
resolvePredSynonyms = resolveTypeSynonyms
#else
resolvePredSynonyms (ClassP n ts) = do
mbInfo <- reifyMaybe n
case mbInfo of
Just (TyConI (TySynD _ synvars def))
-> resolvePredSynonyms $ typeToPred $ expandSynonymRHS synvars ts def
_ -> ClassP n <$> mapM resolveTypeSynonyms ts
resolvePredSynonyms (EqualP t1 t2) = do
t1' <- resolveTypeSynonyms t1
t2' <- resolveTypeSynonyms t2
return (EqualP t1' t2')
typeToPred :: Type -> Pred
typeToPred t =
let f :| xs = decomposeType t in
case f of
ConT n
| n == eqTypeName
# if __GLASGOW_HASKELL__ == 704
, [_,t1,t2] <- xs
# else
, [t1,t2] <- xs
# endif
-> EqualP t1 t2
| otherwise
-> ClassP n xs
_ -> error $ "typeToPred: Can't handle type " ++ show t
#endif
decomposeType :: Type -> NonEmpty Type
decomposeType = go []
where
go args (AppT f x) = go (x:args) f
#if MIN_VERSION_template_haskell(2,11,0)
go args (ParensT t) = go args t
#endif
go args t = t :| args
data NonEmpty a = a :| [a]
data NonEmptySnoc a = [a] :|- a
uncurryType :: Type -> (Cxt, NonEmptySnoc Type)
uncurryType = go [] []
where
go ctxt args (AppT (AppT ArrowT t1) t2) = go ctxt (t1:args) t2
go ctxt args (ForallT _ ctxt' t) = go (ctxt++ctxt') args t
go ctxt args t = (ctxt, reverse args :|- t)
resolveInfixT :: Type -> Q Type
#if MIN_VERSION_template_haskell(2,11,0)
resolveInfixT (ForallT vs cx t) = forallT vs (mapM resolveInfixT cx) (resolveInfixT t)
resolveInfixT (f `AppT` x) = resolveInfixT f `appT` resolveInfixT x
resolveInfixT (ParensT t) = resolveInfixT t
resolveInfixT (InfixT l o r) = conT o `appT` resolveInfixT l `appT` resolveInfixT r
resolveInfixT (SigT t k) = SigT <$> resolveInfixT t <*> resolveInfixT k
resolveInfixT t@UInfixT{} = resolveInfixT =<< resolveInfixT1 (gatherUInfixT t)
resolveInfixT t = return t
gatherUInfixT :: Type -> InfixList
gatherUInfixT (UInfixT l o r) = ilAppend (gatherUInfixT l) o (gatherUInfixT r)
gatherUInfixT t = ILNil t
resolveInfixT1 :: InfixList -> TypeQ
resolveInfixT1 = go []
where
go :: [(Type,Name,Fixity)] -> InfixList -> TypeQ
go ts (ILNil u) = return (foldl (\acc (l,o,_) -> ConT o `AppT` l `AppT` acc) u ts)
go ts (ILCons l o r) =
do ofx <- fromMaybe defaultFixity <$> reifyFixityCompat o
let push = go ((l,o,ofx):ts) r
case ts of
(l1,o1,o1fx):ts' ->
case compareFixity o1fx ofx of
Just True -> go ((ConT o1 `AppT` l1 `AppT` l, o, ofx):ts') r
Just False -> push
Nothing -> fail (precedenceError o1 o1fx o ofx)
_ -> push
compareFixity :: Fixity -> Fixity -> Maybe Bool
compareFixity (Fixity n1 InfixL) (Fixity n2 InfixL) = Just (n1 >= n2)
compareFixity (Fixity n1 InfixR) (Fixity n2 InfixR) = Just (n1 > n2)
compareFixity (Fixity n1 _ ) (Fixity n2 _ ) =
case compare n1 n2 of
GT -> Just True
LT -> Just False
EQ -> Nothing
precedenceError :: Name -> Fixity -> Name -> Fixity -> String
precedenceError o1 ofx1 o2 ofx2 =
"Precedence parsing error: cannot mix ‘" ++
nameBase o1 ++ "’ [" ++ showFixity ofx1 ++ "] and ‘" ++
nameBase o2 ++ "’ [" ++ showFixity ofx2 ++
"] in the same infix type expression"
data InfixList = ILCons Type Name InfixList | ILNil Type
ilAppend :: InfixList -> Name -> InfixList -> InfixList
ilAppend (ILNil l) o r = ILCons l o r
ilAppend (ILCons l1 o1 r1) o r = ILCons l1 o1 (ilAppend r1 o r)
#else
resolveInfixT = return
#endif
showFixity :: Fixity -> String
showFixity (Fixity n d) = showFixityDirection d ++ " " ++ show n
showFixityDirection :: FixityDirection -> String
showFixityDirection InfixL = "infixl"
showFixityDirection InfixR = "infixr"
showFixityDirection InfixN = "infix"
tvName :: TyVarBndr -> Name
tvName (PlainTV name ) = name
tvName (KindedTV name _) = name
takeFieldNames :: [(Name,a,b)] -> [Name]
takeFieldNames xs = [a | (a,_,_) <- xs]
#if MIN_VERSION_template_haskell(2,11,0)
takeFieldStrictness :: [(a,Bang,b)] -> [FieldStrictness]
#else
takeFieldStrictness :: [(a,Strict,b)] -> [FieldStrictness]
#endif
takeFieldStrictness xs = [normalizeStrictness a | (_,a,_) <- xs]
takeFieldTypes :: [(a,b,Type)] -> [Type]
takeFieldTypes xs = [a | (_,_,a) <- xs]
conHasRecord :: Name -> ConstructorInfo -> Bool
conHasRecord recName info =
case constructorVariant info of
NormalConstructor -> False
InfixConstructor -> False
RecordConstructor fields -> recName `elem` fields
quantifyType :: Type -> Type
quantifyType t
| null tvbs
= t
| ForallT tvbs' ctxt' t' <- t
= ForallT (tvbs ++ tvbs') ctxt' t'
| otherwise
= ForallT tvbs [] t
where
tvbs = freeVariablesWellScoped [t]
freeVariablesWellScoped :: [Type] -> [TyVarBndr]
freeVariablesWellScoped tys =
let fvs :: [Name]
fvs = freeVariables tys
varKindSigs :: Map Name Kind
varKindSigs = foldMap go_ty tys
where
go_ty :: Type -> Map Name Kind
go_ty (ForallT tvbs ctxt t) =
foldr (\tvb -> Map.delete (tvName tvb))
(foldMap go_pred ctxt `mappend` go_ty t) tvbs
go_ty (AppT t1 t2) = go_ty t1 `mappend` go_ty t2
go_ty (SigT t k) =
let kSigs =
#if MIN_VERSION_template_haskell(2,8,0)
go_ty k
#else
mempty
#endif
in case t of
VarT n -> Map.insert n k kSigs
_ -> go_ty t `mappend` kSigs
go_ty _ = mempty
go_pred :: Pred -> Map Name Kind
#if MIN_VERSION_template_haskell(2,10,0)
go_pred = go_ty
#else
go_pred (ClassP _ ts) = foldMap go_ty ts
go_pred (EqualP t1 t2) = go_ty t1 `mappend` go_ty t2
#endif
scopedSort :: [Name] -> [Name]
scopedSort = go [] []
go :: [Name]
-> [Set Name]
-> [Name]
-> [Name]
go acc _fv_list [] = reverse acc
go acc fv_list (tv:tvs)
= go acc' fv_list' tvs
where
(acc', fv_list') = insert tv acc fv_list
insert :: Name
-> [Name]
-> [Set Name]
-> ([Name], [Set Name])
insert tv [] [] = ([tv], [kindFVSet tv])
insert tv (a:as) (fvs:fvss)
| tv `Set.member` fvs
, (as', fvss') <- insert tv as fvss
= (a:as', fvs `Set.union` fv_tv : fvss')
| otherwise
= (tv:a:as, fvs `Set.union` fv_tv : fvs : fvss)
where
fv_tv = kindFVSet tv
insert _ _ _ = error "scopedSort"
kindFVSet n =
maybe Set.empty (Set.fromList . freeVariables) (Map.lookup n varKindSigs)
ascribeWithKind n =
maybe (PlainTV n) (KindedTV n) (Map.lookup n varKindSigs)
isKindBinderOnOldGHCs
#if __GLASGOW_HASKELL__ >= 800
= const False
#else
= (`elem` kindVars)
where
kindVars = freeVariables $ Map.elems varKindSigs
#endif
in map ascribeWithKind $
filter (not . isKindBinderOnOldGHCs) $
scopedSort fvs
freshenFreeVariables :: Type -> Q Type
freshenFreeVariables t =
do let xs = [ (n, VarT <$> newName (nameBase n)) | n <- freeVariables t]
subst <- T.sequence (Map.fromList xs)
return (applySubstitution subst t)
class TypeSubstitution a where
applySubstitution :: Map Name Type -> a -> a
freeVariables :: a -> [Name]
instance TypeSubstitution a => TypeSubstitution [a] where
freeVariables = nub . concat . map freeVariables
applySubstitution = fmap . applySubstitution
instance TypeSubstitution Type where
applySubstitution subst = go
where
go (ForallT tvs context t) =
let subst' = foldl' (flip Map.delete) subst (map tvName tvs)
mapTvbKind :: (Kind -> Kind) -> TyVarBndr -> TyVarBndr
mapTvbKind f (PlainTV n) = PlainTV n
mapTvbKind f (KindedTV n k) = KindedTV n (f k) in
ForallT (map (mapTvbKind (applySubstitution subst')) tvs)
(applySubstitution subst' context)
(applySubstitution subst' t)
go (AppT f x) = AppT (go f) (go x)
go (SigT t k) = SigT (go t) (applySubstitution subst k)
go (VarT v) = Map.findWithDefault (VarT v) v subst
#if MIN_VERSION_template_haskell(2,11,0)
go (InfixT l c r) = InfixT (go l) c (go r)
go (UInfixT l c r) = UInfixT (go l) c (go r)
go (ParensT t) = ParensT (go t)
#endif
go t = t
freeVariables t =
case t of
ForallT tvs context t' ->
(concatMap (freeVariables . tvKind) tvs
`union` freeVariables context
`union` freeVariables t')
\\ map tvName tvs
AppT f x -> freeVariables f `union` freeVariables x
SigT t' k -> freeVariables t' `union` freeVariables k
VarT v -> [v]
#if MIN_VERSION_template_haskell(2,11,0)
InfixT l _ r -> freeVariables l `union` freeVariables r
UInfixT l _ r -> freeVariables l `union` freeVariables r
ParensT t' -> freeVariables t'
#endif
_ -> []
instance TypeSubstitution ConstructorInfo where
freeVariables ci =
(freeVariables (constructorContext ci) `union`
freeVariables (constructorFields ci))
\\ (tvName <$> constructorVars ci)
applySubstitution subst ci =
let subst' = foldl' (flip Map.delete) subst (map tvName (constructorVars ci)) in
ci { constructorContext = applySubstitution subst' (constructorContext ci)
, constructorFields = applySubstitution subst' (constructorFields ci)
}
#if !MIN_VERSION_template_haskell(2,10,0)
instance TypeSubstitution Pred where
freeVariables (ClassP _ xs) = freeVariables xs
freeVariables (EqualP x y) = freeVariables x `union` freeVariables y
applySubstitution p (ClassP n xs) = ClassP n (applySubstitution p xs)
applySubstitution p (EqualP x y) = EqualP (applySubstitution p x)
(applySubstitution p y)
#endif
#if !MIN_VERSION_template_haskell(2,8,0)
instance TypeSubstitution Kind where
freeVariables _ = []
applySubstitution _ k = k
#endif
substTyVarBndrs :: Map Name Type -> [TyVarBndr] -> [TyVarBndr]
substTyVarBndrs subst = map go
where
go tvb@(PlainTV {}) = tvb
go (KindedTV n k) = KindedTV n (applySubstitution subst k)
combineSubstitutions :: Map Name Type -> Map Name Type -> Map Name Type
combineSubstitutions x y = Map.union (fmap (applySubstitution y) x) y
unifyTypes :: [Type] -> Q (Map Name Type)
unifyTypes [] = return Map.empty
unifyTypes (t:ts) =
do t':ts' <- mapM resolveTypeSynonyms (t:ts)
let aux sub u =
do sub' <- unify' (applySubstitution sub t')
(applySubstitution sub u)
return (combineSubstitutions sub sub')
case foldM aux Map.empty ts' of
Right m -> return m
Left (x,y) ->
fail $ showString "Unable to unify types "
. showsPrec 11 x
. showString " and "
. showsPrec 11 y
$ ""
unify' :: Type -> Type -> Either (Type,Type) (Map Name Type)
unify' (VarT n) (VarT m) | n == m = pure Map.empty
unify' (VarT n) t | n `elem` freeVariables t = Left (VarT n, t)
| otherwise = Right (Map.singleton n t)
unify' t (VarT n) | n `elem` freeVariables t = Left (VarT n, t)
| otherwise = Right (Map.singleton n t)
unify' (AppT f1 x1) (AppT f2 x2) =
do sub1 <- unify' f1 f2
sub2 <- unify' (applySubstitution sub1 x1) (applySubstitution sub1 x2)
Right (combineSubstitutions sub1 sub2)
unify' (SigT t _) u = unify' t u
unify' t (SigT u _) = unify' t u
unify' t u
| t == u = Right Map.empty
| otherwise = Left (t,u)
equalPred :: Type -> Type -> Pred
equalPred x y =
#if MIN_VERSION_template_haskell(2,10,0)
AppT (AppT EqualityT x) y
#else
EqualP x y
#endif
classPred :: Name -> [Type] -> Pred
classPred =
#if MIN_VERSION_template_haskell(2,10,0)
foldl AppT . ConT
#else
ClassP
#endif
asEqualPred :: Pred -> Maybe (Type,Type)
#if MIN_VERSION_template_haskell(2,10,0)
asEqualPred (EqualityT `AppT` x `AppT` y) = Just (x,y)
asEqualPred (ConT eq `AppT` x `AppT` y) | eq == eqTypeName = Just (x,y)
#else
asEqualPred (EqualP x y) = Just (x,y)
#endif
asEqualPred _ = Nothing
asClassPred :: Pred -> Maybe (Name, [Type])
#if MIN_VERSION_template_haskell(2,10,0)
asClassPred t =
case decomposeType t of
ConT f :| xs | f /= eqTypeName -> Just (f,xs)
_ -> Nothing
#else
asClassPred (ClassP f xs) = Just (f,xs)
asClassPred _ = Nothing
#endif
type IsReifiedDec = Bool
isReified, isn'tReified :: IsReifiedDec
isReified = True
isn'tReified = False
giveTypesStarKinds :: DatatypeInfo -> DatatypeInfo
giveTypesStarKinds info =
info { datatypeVars = annotateVars (datatypeVars info) }
where
annotateVars :: [Type] -> [Type]
annotateVars = map $ \t ->
case t of
VarT n -> SigT (VarT n) starK
_ -> t
giveTyVarBndrsStarKinds :: ConstructorInfo -> ConstructorInfo
giveTyVarBndrsStarKinds info =
info { constructorVars = annotateVars (constructorVars info) }
where
annotateVars :: [TyVarBndr] -> [TyVarBndr]
annotateVars = map $ \tvb ->
case tvb of
PlainTV n -> KindedTV n starK
_ -> tvb
repair13618 :: DatatypeInfo -> Q DatatypeInfo
repair13618 info =
do s <- T.sequence (Map.fromList substList)
return info { datatypeCons = applySubstitution s (datatypeCons info) }
where
used = freeVariables (datatypeCons info)
bound = freeVariables (datatypeVars info)
free = used \\ bound
substList =
[ (u, substEntry u vs)
| u <- free
, let vs = [v | v <- bound, nameBase v == nameBase u]
]
substEntry _ [v] = varT v
substEntry u [] = fail ("Impossible free variable: " ++ show u)
substEntry u _ = fail ("Ambiguous free variable: " ++ show u)
dataDCompat ::
CxtQ ->
Name ->
[TyVarBndr] ->
[ConQ] ->
[Name] ->
DecQ
#if MIN_VERSION_template_haskell(2,12,0)
dataDCompat c n ts cs ds =
dataD c n ts Nothing cs
(if null ds then [] else [derivClause Nothing (map conT ds)])
#elif MIN_VERSION_template_haskell(2,11,0)
dataDCompat c n ts cs ds =
dataD c n ts Nothing cs
(return (map ConT ds))
#else
dataDCompat = dataD
#endif
newtypeDCompat ::
CxtQ ->
Name ->
[TyVarBndr] ->
ConQ ->
[Name] ->
DecQ
#if MIN_VERSION_template_haskell(2,12,0)
newtypeDCompat c n ts cs ds =
newtypeD c n ts Nothing cs
(if null ds then [] else [derivClause Nothing (map conT ds)])
#elif MIN_VERSION_template_haskell(2,11,0)
newtypeDCompat c n ts cs ds =
newtypeD c n ts Nothing cs
(return (map ConT ds))
#else
newtypeDCompat = newtypeD
#endif
tySynInstDCompat ::
Name ->
[TypeQ] ->
TypeQ ->
DecQ
#if MIN_VERSION_template_haskell(2,9,0)
tySynInstDCompat n ps r = TySynInstD n <$> (TySynEqn <$> sequence ps <*> r)
#else
tySynInstDCompat = tySynInstD
#endif
pragLineDCompat ::
Int ->
String ->
Maybe DecQ
#if MIN_VERSION_template_haskell(2,10,0)
pragLineDCompat ln fn = Just (pragLineD ln fn)
#else
pragLineDCompat _ _ = Nothing
#endif
arrowKCompat :: Kind -> Kind -> Kind
#if MIN_VERSION_template_haskell(2,8,0)
arrowKCompat x y = arrowK `appK` x `appK` y
#else
arrowKCompat = arrowK
#endif
reifyFixityCompat :: Name -> Q (Maybe Fixity)
#if MIN_VERSION_template_haskell(2,11,0)
reifyFixityCompat n = recover (return Nothing) ((`mplus` Just defaultFixity) <$> reifyFixity n)
#else
reifyFixityCompat n = recover (return Nothing) $
do info <- reify n
return $! case info of
ClassOpI _ _ _ fixity -> Just fixity
DataConI _ _ _ fixity -> Just fixity
VarI _ _ _ fixity -> Just fixity
_ -> Nothing
#endif
reifyMaybe :: Name -> Q (Maybe Info)
reifyMaybe n = return Nothing `recover` fmap Just (reify n)