module TextShow.TH.Internal (
deriveTextShow
, deriveTextShow1
, deriveTextShow2
, makeShowt
, makeShowtl
, makeShowtPrec
, makeShowtlPrec
, makeShowtList
, makeShowtlList
, makeShowb
, makeShowbPrec
, makeShowbList
, makePrintT
, makePrintTL
, makeHPrintT
, makeHPrintTL
, makeLiftShowbPrec
, makeShowbPrec1
, makeLiftShowbPrec2
, makeShowbPrec2
, Options(..)
, defaultOptions
, GenTextMethods(..)
, deriveTextShowOptions
, deriveTextShow1Options
, deriveTextShow2Options
) where
import Control.Monad (liftM, unless, when)
#if MIN_VERSION_template_haskell(2,11,0)
import Control.Monad ((<=<))
#endif
import Data.Foldable.Compat
import Data.List.Compat
import qualified Data.List.NonEmpty as NE (drop, length, reverse, splitAt)
import Data.List.NonEmpty (NonEmpty(..), (<|))
import qualified Data.Map as Map (fromList, findWithDefault, keys, lookup, singleton)
import Data.Map (Map)
import Data.Maybe
import Data.Monoid.Compat ((<>))
import qualified Data.Set as Set
import Data.Set (Set)
import qualified Data.Text as TS
import qualified Data.Text.IO as TS (putStrLn, hPutStrLn)
import Data.Text.Lazy (toStrict)
import qualified Data.Text.Lazy.Builder as TB
import Data.Text.Lazy.Builder (Builder, toLazyText)
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.IO as TL (putStrLn, hPutStrLn)
import GHC.Exts (Char(..), Double(..), Float(..), Int(..), Word(..))
import GHC.Prim (Char#, Double#, Float#, Int#, Word#)
import GHC.Show (appPrec, appPrec1)
import Language.Haskell.TH.Lib
import Language.Haskell.TH.Ppr hiding (appPrec)
import Language.Haskell.TH.Syntax
import Prelude ()
import Prelude.Compat
import TextShow.Classes (TextShow(..), TextShow1(..), TextShow2(..),
showbListWith, showbParen, showbSpace,
showtParen, showtSpace, showtlParen, showtlSpace)
import TextShow.Options (Options(..), GenTextMethods(..), defaultOptions)
import TextShow.Utils (isInfixDataCon, isSymVar, isTupleString)
deriveTextShow :: Name -> Q [Dec]
deriveTextShow = deriveTextShowOptions defaultOptions
deriveTextShowOptions :: Options -> Name -> Q [Dec]
deriveTextShowOptions = deriveTextShowClass TextShow
deriveTextShow1 :: Name -> Q [Dec]
deriveTextShow1 = deriveTextShow1Options defaultOptions
deriveTextShow1Options :: Options -> Name -> Q [Dec]
deriveTextShow1Options = deriveTextShowClass TextShow1
deriveTextShow2 :: Name -> Q [Dec]
deriveTextShow2 = deriveTextShow2Options defaultOptions
deriveTextShow2Options :: Options -> Name -> Q [Dec]
deriveTextShow2Options = deriveTextShowClass TextShow2
makeShowt :: Name -> Q Exp
makeShowt name = makeShowtPrec name `appE` integerE 0
makeShowtl :: Name -> Q Exp
makeShowtl name = makeShowtlPrec name `appE` integerE 0
makeShowtPrec :: Name -> Q Exp
makeShowtPrec = makeShowbPrecClass TextShow ShowtPrec
makeShowtlPrec :: Name -> Q Exp
makeShowtlPrec = makeShowbPrecClass TextShow ShowtlPrec
makeShowtList :: Name -> Q Exp
makeShowtList name = [| toStrict . $(makeShowtlList name) |]
makeShowtlList :: Name -> Q Exp
makeShowtlList name = [| toLazyText . $(makeShowbList name) |]
makeShowb :: Name -> Q Exp
makeShowb name = makeShowbPrec name `appE` integerE 0
makeShowbPrec :: Name -> Q Exp
makeShowbPrec = makeShowbPrecClass TextShow ShowbPrec
makeLiftShowbPrec :: Name -> Q Exp
makeLiftShowbPrec = makeShowbPrecClass TextShow1 ShowbPrec
makeShowbPrec1 :: Name -> Q Exp
makeShowbPrec1 name = [| $(makeLiftShowbPrec name) showbPrec showbList |]
makeLiftShowbPrec2 :: Name -> Q Exp
makeLiftShowbPrec2 = makeShowbPrecClass TextShow2 ShowbPrec
makeShowbPrec2 :: Name -> Q Exp
makeShowbPrec2 name = [| $(makeLiftShowbPrec2 name) showbPrec showbList showbPrec showbList |]
makeShowbList :: Name -> Q Exp
makeShowbList name = [| showbListWith $(makeShowb name) |]
makePrintT :: Name -> Q Exp
makePrintT name = [| TS.putStrLn . $(makeShowt name) |]
makePrintTL :: Name -> Q Exp
makePrintTL name = [| TL.putStrLn . $(makeShowtl name) |]
makeHPrintT :: Name -> Q Exp
makeHPrintT name = [| \h -> TS.hPutStrLn h . $(makeShowt name) |]
makeHPrintTL :: Name -> Q Exp
makeHPrintTL name = [| \h -> TL.hPutStrLn h . $(makeShowtl name) |]
deriveTextShowClass :: TextShowClass -> Options -> Name -> Q [Dec]
deriveTextShowClass tsClass opts name = withType name fromCons
where
fromCons :: Name -> Cxt -> [TyVarBndr] -> [Con] -> Maybe [Type] -> Q [Dec]
fromCons name' ctxt tvbs cons mbTys = (:[]) <$> do
(instanceCxt, instanceType)
<- buildTypeInstance tsClass name' ctxt tvbs mbTys
instanceD (return instanceCxt)
(return instanceType)
(showbPrecDecs tsClass opts cons)
showbPrecDecs :: TextShowClass -> Options -> [Con] -> [Q Dec]
showbPrecDecs tsClass opts cons =
[genMethod ShowbPrec (showbPrecName tsClass)]
++ if tsClass == TextShow && shouldGenTextMethods
then [genMethod ShowtPrec 'showtPrec, genMethod ShowtlPrec 'showtlPrec]
else []
where
shouldGenTextMethods :: Bool
shouldGenTextMethods = case genTextMethods opts of
AlwaysTextMethods -> True
SometimesTextMethods -> all isNullaryCon cons
NeverTextMethods -> False
genMethod :: TextShowFun -> Name -> Q Dec
genMethod method methodName
= funD methodName
[ clause []
(normalB $ makeTextShowForCons tsClass method cons)
[]
]
makeShowbPrecClass :: TextShowClass -> TextShowFun -> Name -> Q Exp
makeShowbPrecClass tsClass tsFun name = withType name fromCons
where
fromCons :: Name -> Cxt -> [TyVarBndr] -> [Con] -> Maybe [Type] -> Q Exp
fromCons name' ctxt tvbs cons mbTys =
buildTypeInstance tsClass name' ctxt tvbs mbTys
`seq` makeTextShowForCons tsClass tsFun cons
makeTextShowForCons :: TextShowClass -> TextShowFun -> [Con] -> Q Exp
makeTextShowForCons _ _ [] = error "Must have at least one data constructor"
makeTextShowForCons tsClass tsFun cons = do
p <- newName "p"
value <- newName "value"
sps <- newNameList "sp" $ fromEnum tsClass
sls <- newNameList "sl" $ fromEnum tsClass
let spls = zip sps sls
spsAndSls = interleave sps sls
matches <- concatMapM (makeTextShowForCon p tsClass tsFun spls) cons
lamE (map varP $ spsAndSls ++ [p, value])
. appsE
$ [ varE $ showPrecConstName tsClass tsFun
, caseE (varE value) (map return matches)
] ++ map varE spsAndSls
++ [varE p, varE value]
makeTextShowForCon :: Name
-> TextShowClass
-> TextShowFun
-> [(Name, Name)]
-> Con
-> Q [Match]
makeTextShowForCon _ _ tsFun _ (NormalC conName []) = do
m <- match
(conP conName [])
(normalB $ varE (fromStringName tsFun) `appE` stringE (parenInfixConName conName ""))
[]
return [m]
makeTextShowForCon p tsClass tsFun spls (NormalC conName [_]) = do
([argTy], tvMap) <- reifyConTys tsClass spls conName
arg <- newName "arg"
let showArg = makeTextShowForArg appPrec1 tsClass tsFun conName tvMap argTy arg
namedArg = infixApp (varE (fromStringName tsFun) `appE` stringE (parenInfixConName conName " "))
[| (<>) |]
showArg
m <- match
(conP conName [varP arg])
(normalB $ varE (showParenName tsFun)
`appE` infixApp (varE p) [| (>) |] (integerE appPrec)
`appE` namedArg)
[]
return [m]
makeTextShowForCon p tsClass tsFun spls (NormalC conName _) = do
(argTys, tvMap) <- reifyConTys tsClass spls conName
args <- newNameList "arg" $ length argTys
m <- if isNonUnitTuple conName
then do
let showArgs = zipWith (makeTextShowForArg 0 tsClass tsFun conName tvMap) argTys args
parenCommaArgs = (varE (singletonName tsFun) `appE` charE '(')
: intersperse (varE (singletonName tsFun) `appE` charE ',') showArgs
mappendArgs = foldr' (`infixApp` [| (<>) |])
(varE (singletonName tsFun) `appE` charE ')')
parenCommaArgs
match (conP conName $ map varP args)
(normalB mappendArgs)
[]
else do
let showArgs = zipWith (makeTextShowForArg appPrec1 tsClass tsFun conName tvMap) argTys args
mappendArgs = foldr1 (\v q -> infixApp v
[| (<>) |]
(infixApp (varE $ showSpaceName tsFun)
[| (<>) |]
q)) showArgs
namedArgs = infixApp (varE (fromStringName tsFun) `appE` stringE (parenInfixConName conName " "))
[| (<>) |]
mappendArgs
match (conP conName $ map varP args)
(normalB $ varE (showParenName tsFun)
`appE` infixApp (varE p) [| (>) |] (integerE appPrec)
`appE` namedArgs)
[]
return [m]
makeTextShowForCon p tsClass tsFun spls (RecC conName []) =
makeTextShowForCon p tsClass tsFun spls $ NormalC conName []
makeTextShowForCon p tsClass tsFun spls (RecC conName ts) = do
(argTys, tvMap) <- reifyConTys tsClass spls conName
args <- newNameList "arg" $ length argTys
let showArgs = concatMap (\((argName, _, _), argTy, arg)
-> let argNameBase = nameBase argName
infixRec = showParen (isSymVar argNameBase)
(showString argNameBase) ""
in [ varE (fromStringName tsFun) `appE` stringE (infixRec ++ " = ")
, makeTextShowForArg 0 tsClass tsFun conName tvMap argTy arg
, varE (fromStringName tsFun) `appE` stringE ", "
]
)
(zip3 ts argTys args)
braceCommaArgs = (varE (singletonName tsFun) `appE` charE '{') : take (length showArgs 1) showArgs
mappendArgs = foldr' (`infixApp` [| (<>) |])
(varE (singletonName tsFun) `appE` charE '}')
braceCommaArgs
namedArgs = infixApp (varE (fromStringName tsFun) `appE` stringE (parenInfixConName conName " "))
[| (<>) |]
mappendArgs
m <- match
(conP conName $ map varP args)
(normalB $ varE (showParenName tsFun)
`appE` infixApp (varE p) [| (>) |] (integerE appPrec)
`appE` namedArgs)
[]
return [m]
makeTextShowForCon p tsClass tsFun spls (InfixC _ conName _) = do
([alTy, arTy], tvMap) <- reifyConTys tsClass spls conName
al <- newName "argL"
ar <- newName "argR"
info <- reify conName
#if MIN_VERSION_template_haskell(2,11,0)
conPrec <- case info of
DataConI{} -> do
fi <- fromMaybe defaultFixity <$> reifyFixity conName
case fi of
Fixity prec _ -> return prec
#else
let conPrec = case info of
DataConI _ _ _ (Fixity prec _) -> prec
#endif
_ -> error $ "TextShow.TH.makeTextShowForCon: Unsupported type: " ++ show info
let opName = nameBase conName
infixOpE = appE (varE $ fromStringName tsFun) . stringE $
if isInfixDataCon opName
then " " ++ opName ++ " "
else " `" ++ opName ++ "` "
m <- match
(infixP (varP al) conName (varP ar))
(normalB $ (varE (showParenName tsFun) `appE` infixApp (varE p) [| (>) |] (integerE conPrec))
`appE` (infixApp (makeTextShowForArg (conPrec + 1) tsClass tsFun conName tvMap alTy al)
[| (<>) |]
(infixApp infixOpE
[| (<>) |]
(makeTextShowForArg (conPrec + 1) tsClass tsFun conName tvMap arTy ar)))
)
[]
return [m]
makeTextShowForCon p tsClass tsFun spls (ForallC _ _ con) =
makeTextShowForCon p tsClass tsFun spls con
#if MIN_VERSION_template_haskell(2,11,0)
makeTextShowForCon p tsClass tsFun spls (GadtC conNames ts _) =
let con :: Name -> Q Con
con conName = do
mbFi <- reifyFixity conName
return $ if isInfixDataCon (nameBase conName)
&& length ts == 2
&& isJust mbFi
then let [t1, t2] = ts in InfixC t1 conName t2
else NormalC conName ts
in concatMapM (makeTextShowForCon p tsClass tsFun spls <=< con) conNames
makeTextShowForCon p tsClass tsFun spls (RecGadtC conNames ts _) =
concatMapM (makeTextShowForCon p tsClass tsFun spls . flip RecC ts) conNames
#endif
makeTextShowForArg :: Int
-> TextShowClass
-> TextShowFun
-> Name
-> TyVarMap
-> Type
-> Name
-> Q Exp
makeTextShowForArg p _ tsFun _ _ (ConT tyName) tyExpName =
showE
where
tyVarE, showPrecE :: Q Exp
tyVarE = varE tyExpName
showPrecE = varE (showPrecName TextShow tsFun)
showE :: Q Exp
showE | tyName == ''Char# = showPrimE 'C# oneHashE
| tyName == ''Double# = showPrimE 'D# twoHashE
| tyName == ''Float# = showPrimE 'F# oneHashE
| tyName == ''Int# = showPrimE 'I# oneHashE
| tyName == ''Word# = showPrimE 'W# twoHashE
| otherwise = showPrecE `appE` integerE p `appE` tyVarE
showPrimE :: Name -> Q Exp -> Q Exp
showPrimE con _hashE
#if __GLASGOW_HASKELL__ >= 711
= infixApp (showPrecE `appE` integerE 0 `appE` (conE con `appE` tyVarE))
[| (<>) |]
_hashE
#else
= showPrecE `appE` integerE p `appE` (conE con `appE` tyVarE)
#endif
oneHashE, twoHashE :: Q Exp
oneHashE = varE (singletonName tsFun) `appE` charE '#'
twoHashE = varE (fromStringName tsFun) `appE` stringE "##"
makeTextShowForArg p tsClass tsFun conName tvMap ty tyExpName =
[| $(makeTextShowForType tsClass tsFun conName tvMap False ty) p $(varE tyExpName) |]
makeTextShowForType :: TextShowClass
-> TextShowFun
-> Name
-> TyVarMap
-> Bool
-> Type
-> Q Exp
makeTextShowForType _ tsFun _ tvMap sl (VarT tyName) =
varE $ case Map.lookup tyName tvMap of
Just (spExp, slExp) -> if sl then slExp else spExp
Nothing -> if sl then showListName TextShow tsFun
else showPrecName TextShow tsFun
makeTextShowForType tsClass tsFun conName tvMap sl (SigT ty _) =
makeTextShowForType tsClass tsFun conName tvMap sl ty
makeTextShowForType tsClass tsFun conName tvMap sl (ForallT _ _ ty) =
makeTextShowForType tsClass tsFun conName tvMap sl ty
makeTextShowForType tsClass tsFun conName tvMap sl ty = do
let tyCon :: Type
tyArgs :: [Type]
tyCon :| tyArgs = unapplyTy ty
numLastArgs :: Int
numLastArgs = min (fromEnum tsClass) (length tyArgs)
lhsArgs, rhsArgs :: [Type]
(lhsArgs, rhsArgs) = splitAt (length tyArgs numLastArgs) tyArgs
tyVarNames :: [Name]
tyVarNames = Map.keys tvMap
itf <- isTyFamily tyCon
if any (`mentionsName` tyVarNames) lhsArgs
|| itf && any (`mentionsName` tyVarNames) tyArgs
then outOfPlaceTyVarError tsClass conName
else if any (`mentionsName` tyVarNames) rhsArgs
then appsE $ [ varE $ showPrecOrListName sl (toEnum numLastArgs) tsFun]
++ zipWith (makeTextShowForType tsClass tsFun conName tvMap)
(cycle [False,True])
(interleave rhsArgs rhsArgs)
else varE $ if sl then showListName TextShow tsFun
else showPrecName TextShow tsFun
withType :: Name
-> (Name -> Cxt -> [TyVarBndr] -> [Con] -> Maybe [Type] -> Q a)
-> Q a
withType name f = do
info <- reify name
case info of
TyConI dec ->
case dec of
DataD ctxt _ tvbs
#if MIN_VERSION_template_haskell(2,11,0)
_
#endif
cons _ -> f name ctxt tvbs cons Nothing
NewtypeD ctxt _ tvbs
#if MIN_VERSION_template_haskell(2,11,0)
_
#endif
con _ -> f name ctxt tvbs [con] Nothing
_ -> error $ ns ++ "Unsupported type: " ++ show dec
#if MIN_VERSION_template_haskell(2,7,0)
# if MIN_VERSION_template_haskell(2,11,0)
DataConI _ _ parentName -> do
# else
DataConI _ _ parentName _ -> do
# endif
parentInfo <- reify parentName
case parentInfo of
# if MIN_VERSION_template_haskell(2,11,0)
FamilyI (DataFamilyD _ tvbs _) decs ->
# else
FamilyI (FamilyD DataFam _ tvbs _) decs ->
# endif
let instDec = flip find decs $ \dec -> case dec of
DataInstD _ _ _
# if MIN_VERSION_template_haskell(2,11,0)
_
# endif
cons _ -> any ((name ==) . constructorName) cons
NewtypeInstD _ _ _
# if MIN_VERSION_template_haskell(2,11,0)
_
# endif
con _ -> name == constructorName con
_ -> error $ ns ++ "Must be a data or newtype instance."
in case instDec of
Just (DataInstD ctxt _ instTys
# if MIN_VERSION_template_haskell(2,11,0)
_
# endif
cons _)
-> f parentName ctxt tvbs cons $ Just instTys
Just (NewtypeInstD ctxt _ instTys
# if MIN_VERSION_template_haskell(2,11,0)
_
# endif
con _)
-> f parentName ctxt tvbs [con] $ Just instTys
_ -> error $ ns ++
"Could not find data or newtype instance constructor."
_ -> error $ ns ++ "Data constructor " ++ show name ++
" is not from a data family instance constructor."
# if MIN_VERSION_template_haskell(2,11,0)
FamilyI DataFamilyD{} _ ->
# else
FamilyI (FamilyD DataFam _ _ _) _ ->
# endif
error $ ns ++
"Cannot use a data family name. Use a data family instance constructor instead."
_ -> error $ ns ++ "The name must be of a plain data type constructor, "
++ "or a data family instance constructor."
#else
DataConI{} -> dataConIError
_ -> error $ ns ++ "The name must be of a plain type constructor."
#endif
where
ns :: String
ns = "TextShow.TH.withType: "
buildTypeInstance :: TextShowClass
-> Name
-> Cxt
-> [TyVarBndr]
-> Maybe [Type]
-> Q (Cxt, Type)
buildTypeInstance tsClass tyConName dataCxt tvbs Nothing =
let varTys :: [Type]
varTys = map tvbToType tvbs
in buildTypeInstanceFromTys tsClass tyConName dataCxt varTys False
buildTypeInstance tsClass parentName dataCxt tvbs (Just instTysAndKinds) = do
#if !(MIN_VERSION_template_haskell(2,8,0)) || MIN_VERSION_template_haskell(2,10,0)
let instTys :: [Type]
instTys = zipWith stealKindForType tvbs instTysAndKinds
#else
let kindVarNames :: [Name]
kindVarNames = nub $ concatMap (tyVarNamesOfType . tvbKind) tvbs
numKindVars :: Int
numKindVars = length kindVarNames
givenKinds, givenKinds' :: [Kind]
givenTys :: [Type]
(givenKinds, givenTys) = splitAt numKindVars instTysAndKinds
givenKinds' = map sanitizeStars givenKinds
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
starKindName :: Name
starKindName = mkNameG_tc "ghc-prim" "GHC.Prim" "*"
xTypeNames <- newNameList "tExtra" (length tvbs length givenTys)
let xTys :: [Type]
xTys = map VarT xTypeNames
substNamesWithKinds :: [(Name, Kind)] -> Type -> Type
substNamesWithKinds nks t = foldr' (uncurry substNameWithKind) t nks
instTys :: [Type]
instTys = map (substNamesWithKinds (zip kindVarNames givenKinds'))
$ zipWith stealKindForType tvbs (givenTys ++ xTys)
#endif
buildTypeInstanceFromTys tsClass parentName dataCxt instTys True
buildTypeInstanceFromTys :: TextShowClass
-> Name
-> Cxt
-> [Type]
-> Bool
-> Q (Cxt, Type)
buildTypeInstanceFromTys tsClass tyConName dataCxt varTysOrig isDataFamily = do
varTysExp <- mapM expandSyn varTysOrig
let remainingLength :: Int
remainingLength = length varTysOrig fromEnum tsClass
droppedTysExp :: [Type]
droppedTysExp = drop remainingLength varTysExp
droppedStarKindStati :: [StarKindStatus]
droppedStarKindStati = map canRealizeKindStar droppedTysExp
when (remainingLength < 0 || any (== NotKindStar) droppedStarKindStati) $
derivingKindError tsClass tyConName
let droppedKindVarNames :: [Name]
droppedKindVarNames = catKindVarNames droppedStarKindStati
varTysExpSubst :: [Type]
varTysExpSubst = map (substNamesWithKindStar droppedKindVarNames) varTysExp
remainingTysExpSubst, droppedTysExpSubst :: [Type]
(remainingTysExpSubst, droppedTysExpSubst) =
splitAt remainingLength varTysExpSubst
droppedTyVarNames :: [Name]
droppedTyVarNames = concatMap tyVarNamesOfType droppedTysExpSubst
unless (all hasKindStar droppedTysExpSubst) $
derivingKindError tsClass tyConName
let preds :: [Maybe Pred]
kvNames :: [[Name]]
kvNames' :: [Name]
(preds, kvNames) = unzip $ map (deriveConstraint tsClass) remainingTysExpSubst
kvNames' = concat kvNames
remainingTysExpSubst' :: [Type]
remainingTysExpSubst' =
map (substNamesWithKindStar kvNames') remainingTysExpSubst
remainingTysOrigSubst :: [Type]
remainingTysOrigSubst =
map (substNamesWithKindStar (union droppedKindVarNames kvNames'))
$ take remainingLength varTysOrig
remainingTysOrigSubst' :: [Type]
remainingTysOrigSubst' =
if isDataFamily
then remainingTysOrigSubst
else map unSigT remainingTysOrigSubst
instanceCxt :: Cxt
instanceCxt = catMaybes preds
instanceType :: Type
instanceType = AppT (ConT $ textShowClassName tsClass)
$ applyTyCon tyConName remainingTysOrigSubst'
when (any (`predMentionsName` droppedTyVarNames) dataCxt) $
datatypeContextError tyConName instanceType
unless (canEtaReduce remainingTysExpSubst' droppedTysExpSubst) $
etaReductionError instanceType
return (instanceCxt, instanceType)
deriveConstraint :: TextShowClass -> Type -> (Maybe Pred, [Name])
deriveConstraint tsClass t
| not (isTyVar t) = (Nothing, [])
| hasKindStar t = (Just (applyClass ''TextShow tName), [])
| otherwise = case hasKindVarChain 1 t of
Just ns | tsClass >= TextShow1
-> (Just (applyClass ''TextShow1 tName), ns)
_ -> case hasKindVarChain 2 t of
Just ns | tsClass == TextShow2
-> (Just (applyClass ''TextShow2 tName), ns)
_ -> (Nothing, [])
where
tName :: Name
tName = varTToName t
derivingKindError :: TextShowClass -> Name -> a
derivingKindError tsClass tyConName = error
. showString "Cannot derive well-kinded instance of form ‘"
. showString className
. showChar ' '
. showParen True
( showString (nameBase tyConName)
. showString " ..."
)
. showString "‘\n\tClass "
. showString className
. showString " expects an argument of kind "
. showString (pprint . createKindChain $ fromEnum tsClass)
$ ""
where
className :: String
className = nameBase $ textShowClassName tsClass
etaReductionError :: Type -> a
etaReductionError instanceType = error $
"Cannot eta-reduce to an instance of form \n\tinstance (...) => "
++ pprint instanceType
datatypeContextError :: Name -> Type -> a
datatypeContextError dataName instanceType = error
. showString "Can't make a derived instance of ‘"
. showString (pprint instanceType)
. showString "‘:\n\tData type ‘"
. showString (nameBase dataName)
. showString "‘ must not have a class context involving the last type argument(s)"
$ ""
outOfPlaceTyVarError :: TextShowClass -> Name -> a
outOfPlaceTyVarError tsClass conName = error
. showString "Constructor ‘"
. showString (nameBase conName)
. showString "‘ must only use its last "
. shows n
. showString " type variable(s) within the last "
. shows n
. showString " argument(s) of a data type"
$ ""
where
n :: Int
n = fromEnum tsClass
#if !(MIN_VERSION_template_haskell(2,7,0))
dataConIError :: a
dataConIError = error
. showString "Cannot use a data constructor."
. showString "\n\t(Note: if you are trying to derive TextShow for a"
. showString "\n\ttype family, use GHC >= 7.4 instead.)"
$ ""
#endif
expandSyn :: Type -> Q Type
expandSyn (ForallT tvs ctx t) = fmap (ForallT tvs ctx) $ expandSyn t
expandSyn t@AppT{} = expandSynApp t []
expandSyn t@ConT{} = expandSynApp t []
expandSyn (SigT t k) = do t' <- expandSyn t
k' <- expandSynKind k
return (SigT t' k')
expandSyn t = return t
expandSynKind :: Kind -> Q Kind
#if MIN_VERSION_template_haskell(2,8,0)
expandSynKind = expandSyn
#else
expandSynKind = return
#endif
expandSynApp :: Type -> [Type] -> Q Type
expandSynApp (AppT t1 t2) ts = do
t2' <- expandSyn t2
expandSynApp t1 (t2':ts)
expandSynApp (ConT n) ts | nameBase n == "[]" = return $ foldl' AppT ListT ts
expandSynApp t@(ConT n) ts = do
info <- reify n
case info of
TyConI (TySynD _ tvs rhs) ->
let (ts', ts'') = splitAt (length tvs) ts
subs = mkSubst tvs ts'
rhs' = substType subs rhs
in expandSynApp rhs' ts''
_ -> return $ foldl' AppT t ts
expandSynApp t ts = do
t' <- expandSyn t
return $ foldl' AppT t' ts
type TypeSubst = Map Name Type
type KindSubst = Map Name Kind
mkSubst :: [TyVarBndr] -> [Type] -> TypeSubst
mkSubst vs ts =
let vs' = map un vs
un (PlainTV v) = v
un (KindedTV v _) = v
in Map.fromList $ zip vs' ts
substType :: TypeSubst -> Type -> Type
substType subs (ForallT v c t) = ForallT v c $ substType subs t
substType subs t@(VarT n) = Map.findWithDefault t n subs
substType subs (AppT t1 t2) = AppT (substType subs t1) (substType subs t2)
substType subs (SigT t k) = SigT (substType subs t)
#if MIN_VERSION_template_haskell(2,8,0)
(substType subs k)
#else
k
#endif
substType _ t = t
substKind :: KindSubst -> Type -> Type
#if MIN_VERSION_template_haskell(2,8,0)
substKind = substType
#else
substKind _ = id
#endif
substNameWithKind :: Name -> Kind -> Type -> Type
substNameWithKind n k = substKind (Map.singleton n k)
substNamesWithKindStar :: [Name] -> Type -> Type
substNamesWithKindStar ns t = foldr' (flip substNameWithKind starK) t ns
data TextShowClass = TextShow | TextShow1 | TextShow2
deriving (Enum, Eq, Ord)
data TextShowFun = ShowbPrec | ShowtPrec | ShowtlPrec
fromStringName :: TextShowFun -> Name
fromStringName ShowbPrec = 'TB.fromString
fromStringName ShowtPrec = 'TS.pack
fromStringName ShowtlPrec = 'TL.pack
singletonName :: TextShowFun -> Name
singletonName ShowbPrec = 'TB.singleton
singletonName ShowtPrec = 'TS.singleton
singletonName ShowtlPrec = 'TL.singleton
showParenName :: TextShowFun -> Name
showParenName ShowbPrec = 'showbParen
showParenName ShowtPrec = 'showtParen
showParenName ShowtlPrec = 'showtlParen
showSpaceName :: TextShowFun -> Name
showSpaceName ShowbPrec = 'showbSpace
showSpaceName ShowtPrec = 'showtSpace
showSpaceName ShowtlPrec = 'showtlSpace
showPrecConstName :: TextShowClass -> TextShowFun -> Name
showPrecConstName tsClass ShowbPrec = showbPrecConstName tsClass
showPrecConstName TextShow ShowtPrec = 'showtPrecConst
showPrecConstName TextShow ShowtlPrec = 'showtlPrecConst
showPrecConstName _ _ = error "showPrecConstName"
showbPrecConstName :: TextShowClass -> Name
showbPrecConstName TextShow = 'showbPrecConst
showbPrecConstName TextShow1 = 'liftShowbPrecConst
showbPrecConstName TextShow2 = 'liftShowbPrec2Const
textShowClassName :: TextShowClass -> Name
textShowClassName TextShow = ''TextShow
textShowClassName TextShow1 = ''TextShow1
textShowClassName TextShow2 = ''TextShow2
showPrecName :: TextShowClass -> TextShowFun -> Name
showPrecName tsClass ShowbPrec = showbPrecName tsClass
showPrecName TextShow ShowtPrec = 'showtPrec
showPrecName TextShow ShowtlPrec = 'showtlPrec
showPrecName _ _ = error "showPrecName"
showbPrecName :: TextShowClass -> Name
showbPrecName TextShow = 'showbPrec
showbPrecName TextShow1 = 'liftShowbPrec
showbPrecName TextShow2 = 'liftShowbPrec2
showListName :: TextShowClass -> TextShowFun -> Name
showListName tsClass ShowbPrec = showbListName tsClass
showListName TextShow ShowtPrec = 'showtPrec
showListName TextShow ShowtlPrec = 'showtlPrec
showListName _ _ = error "showListName"
showbListName :: TextShowClass -> Name
showbListName TextShow = 'showbList
showbListName TextShow1 = 'liftShowbList
showbListName TextShow2 = 'liftShowbList2
showPrecOrListName :: Bool
-> TextShowClass
-> TextShowFun
-> Name
showPrecOrListName False = showPrecName
showPrecOrListName True = showListName
showbPrecConst :: Builder
-> Int -> a -> Builder
showbPrecConst b _ _ = b
showtPrecConst :: TS.Text
-> Int -> a -> TS.Text
showtPrecConst t _ _ = t
showtlPrecConst :: TL.Text
-> Int -> a -> TL.Text
showtlPrecConst tl _ _ = tl
liftShowbPrecConst :: Builder
-> (Int -> a -> Builder) -> ([a] -> Builder)
-> Int -> f a -> Builder
liftShowbPrecConst b _ _ _ _ = b
liftShowbPrec2Const :: Builder
-> (Int -> a -> Builder) -> ([a] -> Builder)
-> (Int -> b -> Builder) -> ([b] -> Builder)
-> Int -> f a b -> Builder
liftShowbPrec2Const b _ _ _ _ _ _ = b
data StarKindStatus = NotKindStar
| KindStar
| IsKindVar Name
deriving Eq
canRealizeKindStar :: Type -> StarKindStatus
canRealizeKindStar t
| hasKindStar t = KindStar
| otherwise = case t of
#if MIN_VERSION_template_haskell(2,8,0)
SigT _ (VarT k) -> IsKindVar k
#endif
_ -> NotKindStar
starKindStatusToName :: StarKindStatus -> Maybe Name
starKindStatusToName (IsKindVar n) = Just n
starKindStatusToName _ = Nothing
catKindVarNames :: [StarKindStatus] -> [Name]
catKindVarNames = mapMaybe starKindStatusToName
integerE :: Int -> Q Exp
integerE = litE . integerL . fromIntegral
charE :: Char -> Q Exp
charE = litE . charL
hasKindStar :: Type -> Bool
hasKindStar VarT{} = True
#if MIN_VERSION_template_haskell(2,8,0)
hasKindStar (SigT _ StarT) = True
#else
hasKindStar (SigT _ StarK) = True
#endif
hasKindStar _ = False
isStarOrVar :: Kind -> Bool
#if MIN_VERSION_template_haskell(2,8,0)
isStarOrVar StarT = True
isStarOrVar VarT{} = True
#else
isStarOrVar StarK = True
#endif
isStarOrVar _ = False
newNameList :: String -> Int -> Q [Name]
newNameList prefix n = mapM (newName . (prefix ++) . show) [1..n]
tyVarNamesOfType :: Type -> [Name]
tyVarNamesOfType = go
where
go :: Type -> [Name]
go (AppT t1 t2) = go t1 ++ go t2
go (SigT t _k) = go t
#if MIN_VERSION_template_haskell(2,8,0)
++ go _k
#endif
go (VarT n) = [n]
go _ = []
tyVarNamesOfKind :: Kind -> [Name]
#if MIN_VERSION_template_haskell(2,8,0)
tyVarNamesOfKind = tyVarNamesOfType
#else
tyVarNamesOfKind _ = []
#endif
hasKindVarChain :: Int -> Type -> Maybe [Name]
hasKindVarChain kindArrows t =
let uk = uncurryKind (tyKind t)
in if (length uk 1 == kindArrows) && all isStarOrVar uk
then Just (concatMap tyVarNamesOfKind uk)
else Nothing
tyKind :: Type -> Kind
tyKind (SigT _ k) = k
tyKind _ = starK
stealKindForType :: TyVarBndr -> Type -> Type
stealKindForType tvb t@VarT{} = SigT t (tvbKind tvb)
stealKindForType _ t = t
concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b]
concatMapM f xs = liftM concat (mapM f xs)
type TyVarMap = Map Name (Name, Name)
isNonUnitTuple :: Name -> Bool
isNonUnitTuple = isTupleString . nameBase
parenInfixConName :: Name -> ShowS
parenInfixConName conName =
let conNameBase = nameBase conName
in showParen (isInfixDataCon conNameBase) $ showString conNameBase
tvbKind :: TyVarBndr -> Kind
tvbKind (PlainTV _) = starK
tvbKind (KindedTV _ k) = k
tvbToType :: TyVarBndr -> Type
tvbToType (PlainTV n) = VarT n
tvbToType (KindedTV n k) = SigT (VarT n) k
applyClass :: Name -> Name -> Pred
#if MIN_VERSION_template_haskell(2,10,0)
applyClass con t = AppT (ConT con) (VarT t)
#else
applyClass con t = ClassP con [VarT t]
#endif
canEtaReduce :: [Type] -> [Type] -> Bool
canEtaReduce remaining dropped =
all isTyVar dropped
&& allDistinct droppedNames
&& not (any (`mentionsName` droppedNames) remaining)
where
droppedNames :: [Name]
droppedNames = map varTToName dropped
varTToName_maybe :: Type -> Maybe Name
varTToName_maybe (VarT n) = Just n
varTToName_maybe (SigT t _) = varTToName_maybe t
varTToName_maybe _ = Nothing
varTToName :: Type -> Name
varTToName = fromMaybe (error "Not a type variable!") . varTToName_maybe
unSigT :: Type -> Type
unSigT (SigT t _) = t
unSigT t = t
isTyVar :: Type -> Bool
isTyVar (VarT _) = True
isTyVar (SigT t _) = isTyVar t
isTyVar _ = False
isTyFamily :: Type -> Q Bool
isTyFamily (ConT n) = do
info <- reify n
return $ case info of
#if MIN_VERSION_template_haskell(2,11,0)
FamilyI OpenTypeFamilyD{} _ -> True
#elif MIN_VERSION_template_haskell(2,7,0)
FamilyI (FamilyD TypeFam _ _ _) _ -> True
#else
TyConI (FamilyD TypeFam _ _ _) -> True
#endif
#if MIN_VERSION_template_haskell(2,9,0)
FamilyI ClosedTypeFamilyD{} _ -> True
#endif
_ -> False
isTyFamily _ = return False
allDistinct :: Ord a => [a] -> Bool
allDistinct = allDistinct' Set.empty
where
allDistinct' :: Ord a => Set a -> [a] -> Bool
allDistinct' uniqs (x:xs)
| x `Set.member` uniqs = False
| otherwise = allDistinct' (Set.insert x uniqs) xs
allDistinct' _ _ = True
mentionsName :: Type -> [Name] -> Bool
mentionsName = go
where
go :: Type -> [Name] -> Bool
go (AppT t1 t2) names = go t1 names || go t2 names
go (SigT t _k) names = go t names
#if MIN_VERSION_template_haskell(2,8,0)
|| go _k names
#endif
go (VarT n) names = n `elem` names
go _ _ = False
predMentionsName :: Pred -> [Name] -> Bool
#if MIN_VERSION_template_haskell(2,10,0)
predMentionsName = mentionsName
#else
predMentionsName (ClassP n tys) names = n `elem` names || any (`mentionsName` names) tys
predMentionsName (EqualP t1 t2) names = mentionsName t1 names || mentionsName t2 names
#endif
applyTy :: Type -> [Type] -> Type
applyTy = foldl' AppT
applyTyCon :: Name -> [Type] -> Type
applyTyCon = applyTy . ConT
unapplyTy :: Type -> NonEmpty Type
unapplyTy = NE.reverse . go
where
go :: Type -> NonEmpty Type
go (AppT t1 t2) = t2 <| go t1
go (SigT t _) = go t
go (ForallT _ _ t) = go t
go t = t :| []
uncurryTy :: Type -> NonEmpty Type
uncurryTy (AppT (AppT ArrowT t1) t2) = t1 <| uncurryTy t2
uncurryTy (SigT t _) = uncurryTy t
uncurryTy (ForallT _ _ t) = uncurryTy t
uncurryTy t = t :| []
uncurryKind :: Kind -> NonEmpty Kind
#if MIN_VERSION_template_haskell(2,8,0)
uncurryKind = uncurryTy
#else
uncurryKind (ArrowK k1 k2) = k1 <| uncurryKind k2
uncurryKind k = k :| []
#endif
createKindChain :: Int -> Kind
createKindChain = go starK
where
go :: Kind -> Int -> Kind
go k !0 = k
#if MIN_VERSION_template_haskell(2,8,0)
go k !n = go (AppT (AppT ArrowT StarT) k) (n 1)
#else
go k !n = go (ArrowK StarK k) (n 1)
#endif
#if MIN_VERSION_template_haskell(2,7,0)
constructorName :: Con -> Name
constructorName (NormalC name _ ) = name
constructorName (RecC name _ ) = name
constructorName (InfixC _ name _ ) = name
constructorName (ForallC _ _ con) = constructorName con
# if MIN_VERSION_template_haskell(2,11,0)
constructorName (GadtC names _ _) = head names
constructorName (RecGadtC names _ _) = head names
# endif
#endif
isNullaryCon :: Con -> Bool
isNullaryCon (NormalC _ []) = True
isNullaryCon (RecC _ []) = True
isNullaryCon InfixC{} = False
isNullaryCon (ForallC _ _ con) = isNullaryCon con
#if MIN_VERSION_template_haskell(2,11,0)
isNullaryCon (GadtC _ [] _) = True
isNullaryCon (RecGadtC _ [] _) = True
#endif
isNullaryCon _ = False
interleave :: [a] -> [a] -> [a]
interleave (a1:a1s) (a2:a2s) = a1:a2:interleave a1s a2s
interleave _ _ = []
reifyConTys :: TextShowClass
-> [(Name, Name)]
-> Name
-> Q ([Type], TyVarMap)
reifyConTys tsClass spls conName = do
info <- reify conName
uncTy <- case info of
DataConI _ ty _
#if !(MIN_VERSION_template_haskell(2,11,0))
_
#endif
-> fmap uncurryTy (expandSyn ty)
_ -> error "Must be a data constructor"
let (argTys, [resTy]) = NE.splitAt (length uncTy 1) uncTy
unapResTy = unapplyTy resTy
mbTvNames = map varTToName_maybe $
NE.drop (NE.length unapResTy fromEnum tsClass) unapResTy
tvMap = Map.fromList
. catMaybes
$ zipWith (\mbTvName sp ->
fmap (\tvName -> (tvName, sp)) mbTvName)
mbTvNames spls
return (argTys, tvMap)