{-# 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
, freshenFreeVariables
, equalPred
, classPred
, asEqualPred
, asClassPred
, dataDCompat
, newtypeDCompat
, tySynInstDCompat
, pragLineDCompat
, arrowKCompat
, isStrictAnnot
, notStrictAnnot
, unpackedAnnot
, resolveTypeSynonyms
, 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.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(..), (<$>))
#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
info <- reifyRecover n $ 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."
]
case info of
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 "normalizeCon: impossible"
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) = mergeArguments params ts
subst = VarT <$> substName
tyvars' = [ tv | tv <- renamedTyvars, Map.notMember (tvName tv) subst ]
context2 = applySubstitution subst (context1 ++ renamedContext)
fields' = applySubstitution subst renamedFields
in sequence [ ConstructorInfo name tyvars' context2
fields' stricts <$> variantQ
| name <- names
, let variantQ = getVariant name
]
_ -> fail "normalizeGadtC: Expected type constructor application"
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
resolveTypeSynonyms :: Type -> Q Type
resolveTypeSynonyms t =
let f :| xs = decomposeType t
notTypeSynCase = foldl AppT f <$> mapM resolveTypeSynonyms xs in
case f of
ConT n ->
do info <- reifyRecover n $ fail
"resolveTypeSynonyms: Cannot reify type synonym information"
case info of
TyConI (TySynD _ synvars def)
-> resolveTypeSynonyms $ expandSynonymRHS synvars xs def
_ -> notTypeSynCase
_ -> notTypeSynCase
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
info <- reifyRecover n $ fail
"resolvePredSynonyms: Cannot reify type synonym information"
case info of
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
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 vs = t
| otherwise = ForallT (PlainTV <$> vs) [] t
where
vs = freeVariables t
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
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
reifyRecover ::
Name ->
Q Info ->
Q Info
reifyRecover n failure = failure `recover` reify n