large-records-0.1.0.0: src/Data/Record/Internal/TH/Util.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE PatternSynonyms #-}
-- | Utility functions for working with TH
module Data.Record.Internal.TH.Util (
-- * Folding
appsT
, arrT
-- * Constructing lists (variations on 'listE')
, vectorE
, plistT
, ptupleT
-- * Simplified construction
, simpleFn
, simplePatSynType
-- * Dealing with type variables
, tyVarName
, tyVarType
-- * Bang
, pattern DefaultBang
-- * Extensions
, requiresExtensions
) where
import Control.Monad
import Data.List (intercalate)
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
import qualified Data.Vector as V
import qualified Data.Record.Internal.TH.Name as N
{-------------------------------------------------------------------------------
Folding
-------------------------------------------------------------------------------}
-- | Repeated application
--
-- @appsT f [x1, .., xN]@ constructs something like
--
-- > f x1 .. xN
appsT :: Q Type -> [Q Type] -> Q Type
appsT t ts = foldl appT t ts
-- | Repeated application of @(->)@
--
-- @arrT [x1, .., xN] y@ constructs something like
--
-- > x1 -> .. -> xN -> y
arrT :: [Q Type] -> Q Type -> Q Type
arrT ts t = foldr (\a b -> arrowT `appT` a `appT` b) t ts
{-------------------------------------------------------------------------------
Constructing lists (variations on 'listE')
-------------------------------------------------------------------------------}
vectorE :: (a -> Q Exp) -> [a] -> Q Exp
vectorE f elems = [| V.fromList $(listE (map f elems)) |]
plistT :: [Q Type] -> Q Type
plistT = foldr cons nil
where
nil = promotedNilT
cons t ts = promotedConsT `appT` t `appT` ts
ptupleT :: [Q Type] -> Q Type
ptupleT ts = appsT (promotedTupleT (length ts)) ts
{-------------------------------------------------------------------------------
Simplified construction
-------------------------------------------------------------------------------}
-- | Construct simple function
--
-- @simpleFn n typ body@ constructs something like
--
-- > f :: typ
-- > f = body
simpleFn :: N.Name 'VarName flavour -> Q Type -> Q Exp -> Q [Dec]
simpleFn fnName qTyp qBody = do
typ <- qTyp
body <- qBody
return [
SigD fnName' typ
, ValD (VarP fnName') (NormalB body) []
]
where
fnName' :: Name
fnName' = N.toTH fnName
-- | Construct simple pattern synonym type
--
-- @simplePatSynType xs [t1, .., tn] s@ constructs something like
--
-- > pattern foo :: forall xs. t1 -> .. -> tn -> s
simplePatSynType :: [TyVarBndr] -> [Q Type] -> Q Type -> Q PatSynType
simplePatSynType tvars fieldTypes resultType =
forallT tvars (cxt [])
$ forallT [] (cxt [])
$ arrT fieldTypes resultType
{-------------------------------------------------------------------------------
Dealing with type variables
-------------------------------------------------------------------------------}
tyVarName :: TyVarBndr -> Name
tyVarName (PlainTV n) = n
tyVarName (KindedTV n _) = n
tyVarType :: TyVarBndr -> Q Type
tyVarType = varT . tyVarName
{-------------------------------------------------------------------------------
Bang
-------------------------------------------------------------------------------}
pattern DefaultBang :: Bang
pattern DefaultBang = Bang NoSourceUnpackedness NoSourceStrictness
{-------------------------------------------------------------------------------
Extensions
-------------------------------------------------------------------------------}
-- | Check that the specified extensions are enabled
--
-- To improve user experience, we report all missing extensions at once (rather
-- than giving an error for the first missing one).
requiresExtensions :: Quasi m => [Extension] -> m ()
requiresExtensions exts = runQ $ do
disabled <- filterM (fmap not . isExtEnabled) exts
unless (null disabled) $ do
fail $ "Please enable " ++ intercalate ", " (map show disabled)