{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
-- | Shared functions for dependent-sum-template
module Data.GADT.TH.Internal where
import Control.Monad
import Control.Monad.Writer
import Data.List (foldl', drop)
import Data.Maybe
import Data.Map (Map)
import qualified Data.Map as Map
import qualified Data.Map.Merge.Lazy as Map
import Data.Set (Set)
import qualified Data.Set as Set
import Language.Haskell.TH
import Language.Haskell.TH.Datatype
import Language.Haskell.TH.Datatype.TyVarBndr
classHeadToParams :: Type -> (Name, [Type])
classHeadToParams t = (h, reverse reversedParams)
where
(h, reversedParams) = go t
go :: Type -> (Name, [Type])
go t = case t of
AppT f x ->
let (h, reversedParams) = classHeadToParams f
in (h, x : reversedParams)
_ -> (headOfType t, [])
-- Do not export this data family, it must remain empty. It's used as a way to trick GHC into not unifying certain type variables.
data family Skolem :: k -> k
skolemize :: Set Name -> Type -> Type
skolemize rigids t = case t of
ForallT bndrs cxt t' -> ForallT bndrs cxt (skolemize (Set.difference rigids (Set.fromList (map tvName bndrs))) t')
AppT t1 t2 -> AppT (skolemize rigids t1) (skolemize rigids t2)
SigT t k -> SigT (skolemize rigids t) k
VarT v -> if Set.member v rigids
then AppT (ConT ''Skolem) (VarT v)
else t
InfixT t1 n t2 -> InfixT (skolemize rigids t1) n (skolemize rigids t2)
UInfixT t1 n t2 -> UInfixT (skolemize rigids t1) n (skolemize rigids t2)
ParensT t -> ParensT (skolemize rigids t)
_ -> t
reifyInstancesWithRigids :: Set Name -> Name -> [Type] -> Q [InstanceDec]
reifyInstancesWithRigids rigids cls tys = reifyInstances cls (map (skolemize rigids) tys)
-- | Determine the type variables which occur freely in a type.
freeTypeVariables :: Type -> Set Name
freeTypeVariables t = case t of
ForallT bndrs _ t' -> Set.difference (freeTypeVariables t') (Set.fromList (map tvName bndrs))
AppT t1 t2 -> Set.union (freeTypeVariables t1) (freeTypeVariables t2)
SigT t _ -> freeTypeVariables t
VarT n -> Set.singleton n
_ -> Set.empty
subst :: Map Name Type -> Type -> Type
subst s = f
where
f = \case
ForallT bndrs cxt t ->
let s' = Map.difference s (Map.fromList [(k,()) | k <- map tvName bndrs])
in ForallT bndrs cxt (subst s' t)
AppT t t' -> AppT (f t) (f t')
SigT t k -> SigT (f t) k
VarT n -> case Map.lookup n s of
Just t -> t
Nothing -> VarT n
InfixT t x t' -> InfixT (f t) x (f t')
UInfixT t x t' -> UInfixT (f t) x (f t')
x -> x
-- Invoke the deriver for the given class instance. We assume that the type
-- we're deriving for is always the first typeclass parameter, if there are
-- multiple.
deriveForDec
:: Name
-> (DatatypeInfo -> WriterT [Type] Q Dec)
-> Dec
-> Q [Dec]
deriveForDec className f (InstanceD overlaps cxt instanceHead decs) = do
let (givenClassName, firstParam : _) = classHeadToParams instanceHead
when (givenClassName /= className) $
fail $ "while deriving " ++ show className ++ ": wrong class name in prototype declaration: " ++ show givenClassName
let dataTypeName = headOfType firstParam
dataTypeInfo <- reifyDatatype dataTypeName
let instTypes = datatypeInstTypes dataTypeInfo
paramVars = Set.unions [freeTypeVariables t | t <- instTypes]
instTypes' = case reverse instTypes of
[] -> fail "deriveGEq: Not enough type parameters"
(_:xs) -> reverse xs
generatedInstanceHead = AppT (ConT className) (foldl AppT (ConT $ datatypeName dataTypeInfo) instTypes')
unifiedTypes <- unifyTypes [generatedInstanceHead, instanceHead]
let
newInstanceHead = applySubstitution unifiedTypes instanceHead
newContext = applySubstitution unifiedTypes cxt
-- We are not using the generated context that we collect from f, instead
-- relying on a correct instance head from the user
(dec, _) <- runWriterT $ f dataTypeInfo
return [InstanceD overlaps newContext newInstanceHead [dec]]
deriveForDec className f dataDec = do
dataTypeInfo <- normalizeDec dataDec
let instTypes = datatypeInstTypes dataTypeInfo
paramVars = Set.unions [freeTypeVariables t | t <- instTypes]
instTypes' = case reverse instTypes of
[] -> fail "deriveGEq: Not enough type parameters"
(_:xs) -> reverse xs
instanceHead = AppT (ConT className) (foldl AppT (ConT $ datatypeName dataTypeInfo) instTypes')
(dec, cxt') <- runWriterT (f dataTypeInfo)
return [InstanceD Nothing (datatypeContext dataTypeInfo ++ cxt') instanceHead [dec]]
headOfType :: Type -> Name
headOfType = \case
ForallT _ _ ty -> headOfType ty
VarT name -> name
ConT name -> name
TupleT n -> tupleTypeName n
ArrowT -> ''(->)
ListT -> ''[]
AppT t _ -> headOfType t