{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Language.Haskell.TH.Lift
( deriveLift
, deriveLiftMany
, deriveLift'
, deriveLiftMany'
, makeLift
, makeLift'
, Lift(..)
) where
#if !(MIN_VERSION_template_haskell(2,4,0))
import Data.PackedString (PackedString, packString, unpackPS)
#endif /* MIN_VERSION_template_haskell(2,4,0) */
import GHC.Base (unpackCString#)
import GHC.Exts (Double(..), Float(..), Int(..), Word(..))
import GHC.Prim (Addr#, Double#, Float#, Int#, Word#)
#if MIN_VERSION_template_haskell(2,11,0)
import GHC.Exts (Char(..))
import GHC.Prim (Char#)
#endif /* !(MIN_VERSION_template_haskell(2,11,0)) */
import Control.Applicative
#if MIN_VERSION_template_haskell(2,8,0)
import Data.Char (ord)
#endif /* !(MIN_VERSION_template_haskell(2,8,0)) */
#if MIN_VERSION_base(4,8,0)
import Data.Functor.Identity
#endif
#if !(MIN_VERSION_template_haskell(2,10,0))
import Data.Ratio (Ratio)
#endif /* !(MIN_VERSION_template_haskell(2,10,0)) */
import Language.Haskell.TH
import Language.Haskell.TH.Datatype
import qualified Language.Haskell.TH.Lib as Lib (starK)
import Language.Haskell.TH.Syntax
import Control.Monad ((<=<), zipWithM)
#if MIN_VERSION_template_haskell(2,9,0)
import Data.Maybe (catMaybes)
#endif /* MIN_VERSION_template_haskell(2,9,0) */
deriveLift :: Name -> Q [Dec]
#if MIN_VERSION_template_haskell(2,9,0)
deriveLift name = do
roles <- reifyDatatypeRoles name
info <- reifyDatatype name
fmap (:[]) $ deriveLiftOne roles info
#else
deriveLift = fmap (:[]) . deriveLiftOne <=< reifyDatatype
#endif
deriveLiftMany :: [Name] -> Q [Dec]
#if MIN_VERSION_template_haskell(2,9,0)
deriveLiftMany names = do
roles <- mapM reifyDatatypeRoles names
infos <- mapM reifyDatatype names
mapM (uncurry deriveLiftOne) $ zip roles infos
#else
deriveLiftMany = mapM deriveLiftOne <=< mapM reifyDatatype
#endif
#if MIN_VERSION_template_haskell(2,9,0)
deriveLift' :: [Role] -> Info -> Q [Dec]
deriveLift' roles = fmap (:[]) . deriveLiftOne roles <=< normalizeInfo
deriveLiftMany' :: [([Role], Info)] -> Q [Dec]
deriveLiftMany' = mapM (\(rs, i) -> deriveLiftOne rs =<< normalizeInfo i)
#else
deriveLift' :: Info -> Q [Dec]
deriveLift' = fmap (:[]) . deriveLiftOne <=< normalizeInfo
deriveLiftMany' :: [Info] -> Q [Dec]
deriveLiftMany' = mapM (deriveLiftOne <=< normalizeInfo)
#endif
makeLift :: Name -> Q Exp
makeLift = makeLiftInternal <=< reifyDatatype
makeLift' :: Info -> Q Exp
makeLift' = makeLiftInternal <=< normalizeInfo
makeLiftInternal :: DatatypeInfo -> Q Exp
makeLiftInternal i = withInfo i $ \_ n _ cons -> makeLiftOne n cons
#if MIN_VERSION_template_haskell(2,9,0)
deriveLiftOne :: [Role] -> DatatypeInfo -> Q Dec
deriveLiftOne roles i = withInfo i liftInstance
#else
deriveLiftOne :: DatatypeInfo -> Q Dec
deriveLiftOne i = withInfo i liftInstance
#endif
where
liftInstance dcx n tys cons = do
#if MIN_VERSION_template_haskell(2,9,0)
let phtys = catMaybes $
zipWith (\t role -> if role == PhantomR then Just t else Nothing)
tys
roles
#else /* MIN_VERSION_template_haskell(2,9,0) */
let phtys = []
#endif
instanceD (ctxt dcx phtys tys)
(conT ''Lift `appT` typ n tys)
[funD 'lift [clause [] (normalB (makeLiftOne n cons)) []]]
typ n = foldl appT (conT n) . map unKind
ctxt dcx phtys =
fmap (dcx ++) . cxt . concatMap liftPred . filter (`notElem` phtys)
liftPred ty =
case ty of
SigT t k
| k == Lib.starK -> mkLift t
| otherwise -> []
_ -> mkLift ty
#if MIN_VERSION_template_haskell(2,10,0)
mkLift ty = [conT ''Lift `appT` (return ty)]
#else
mkLift ty = [classP ''Lift [return ty]]
#endif
unKind (SigT t k)
| k == Lib.starK = return t
unKind t = return t
makeLiftOne :: Name -> [ConstructorInfo] -> Q Exp
makeLiftOne n cons = do
e <- newName "e"
lam1E (varP e) $ caseE (varE e) $ consMatches n cons
consMatches :: Name -> [ConstructorInfo] -> [Q Match]
consMatches n [] = [match wildP (normalB e) []]
where
e = [| errorQExp $(stringE ("Can't lift value of empty datatype " ++ nameBase n)) |]
consMatches _ cons = concatMap doCons cons
doCons :: ConstructorInfo -> [Q Match]
doCons (ConstructorInfo { constructorName = c
, constructorFields = ts
, constructorVariant = variant
}) = (:[]) $ do
ns <- zipWithM (\_ i -> newName ('x':show (i :: Int))) ts [0..]
let con = [| conE c |]
case (variant, ns, ts) of
(InfixConstructor, [x0, x1], [t0, t1]) ->
let e = [| infixApp $(liftVar x0 t0) $con $(liftVar x1 t1) |]
in match (infixP (varP x0) c (varP x1)) (normalB e) []
(_, _, _) ->
let e = foldl (\e1 e2 -> [| appE $e1 $e2 |]) con $ zipWith liftVar ns ts
in match (conP c (map varP ns)) (normalB e) []
#if MIN_VERSION_template_haskell(2,9,0)
reifyDatatypeRoles :: Name -> Q [Role]
reifyDatatypeRoles n = do
DatatypeInfo { datatypeName = dn } <- reifyDatatype n
qReifyRoles dn
#endif
liftVar :: Name -> Type -> Q Exp
liftVar varName (ConT tyName)
#if MIN_VERSION_template_haskell(2,8,0)
| tyName == ''Addr# = [| litE (stringPrimL (map (fromIntegral . ord)
(unpackCString# $var))) |]
#else /* !(MIN_VERSION_template_haskell(2,8,0)) */
| tyName == ''Addr# = [| litE (stringPrimL (unpackCString# $var)) |]
#endif
#if MIN_VERSION_template_haskell(2,11,0)
| tyName == ''Char# = [| litE (charPrimL (C# $var)) |]
#endif /* !(MIN_VERSION_template_haskell(2,11,0)) */
| tyName == ''Double# = [| litE (doublePrimL (toRational (D# $var))) |]
| tyName == ''Float# = [| litE (floatPrimL (toRational (F# $var))) |]
| tyName == ''Int# = [| litE (intPrimL (toInteger (I# $var))) |]
| tyName == ''Word# = [| litE (wordPrimL (toInteger (W# $var))) |]
where
var :: Q Exp
var = varE varName
liftVar varName _ = [| lift $(varE varName) |]
withInfo :: DatatypeInfo
-> (Cxt -> Name -> [Type] -> [ConstructorInfo] -> Q a)
-> Q a
withInfo i f = case i of
DatatypeInfo { datatypeContext = dcx
, datatypeName = n
, datatypeVars = vs
, datatypeCons = cons
} ->
f dcx n vs cons
errorQExp :: String -> Q Exp
errorQExp = error
{-# INLINE errorQExp #-}
instance Lift Name where
lift (Name occName nameFlavour) = [| Name occName nameFlavour |]
#if MIN_VERSION_template_haskell(2,4,0)
instance Lift OccName where
lift n = [| mkOccName $(lift $ occString n) |]
instance Lift PkgName where
lift n = [| mkPkgName $(lift $ pkgString n) |]
instance Lift ModName where
lift n = [| mkModName $(lift $ modString n) |]
#else /* MIN_VERSION_template_haskell(2,4,0) */
instance Lift PackedString where
lift ps = [| packString $(lift $ unpackPS ps) |]
#endif /* MIN_VERSION_template_haskell(2,4,0) */
instance Lift NameFlavour where
lift NameS = [| NameS |]
lift (NameQ modnam) = [| NameQ modnam |]
#if __GLASGOW_HASKELL__ >= 710
lift (NameU i) = [| NameU i |]
lift (NameL i) = [| NameL i |]
#else /* __GLASGOW_HASKELL__ < 710 */
lift (NameU i) = [| case $( lift (I# i) ) of
I# i' -> NameU i' |]
lift (NameL i) = [| case $( lift (I# i) ) of
I# i' -> NameL i' |]
#endif /* __GLASGOW_HASKELL__ < 710 */
lift (NameG nameSpace' pkgName modnam)
= [| NameG nameSpace' pkgName modnam |]
instance Lift NameSpace where
lift VarName = [| VarName |]
lift DataName = [| DataName |]
lift TcClsName = [| TcClsName |]
#if !(MIN_VERSION_template_haskell(2,10,0))
instance Lift () where
lift _ = [| () |]
instance Integral a => Lift (Ratio a) where
lift x = return (LitE (RationalL (toRational x)))
#endif
#if MIN_VERSION_base(4,8,0)
instance Lift a => Lift (Identity a) where
lift = appE (conE 'Identity) . lift . runIdentity
#endif
instance Lift a => Lift (Const a b) where
lift = appE (conE 'Const) . lift . getConst