我需要将编译脚本中的一些信息传递到Template Haskell中。目前,编译脚本将信息存储在系统环境中,因此我只需使用runIO
包装的System.Environment.getEnvironment
来读取它。是否有更好的方法,例如传递一些参数给ghc
(类似于C预处理器的-D...
),或者在TH中专门为此目的设计的东西?
我需要将编译脚本中的一些信息传递到Template Haskell中。目前,编译脚本将信息存储在系统环境中,因此我只需使用runIO
包装的System.Environment.getEnvironment
来读取它。是否有更好的方法,例如传递一些参数给ghc
(类似于C预处理器的-D...
),或者在TH中专门为此目的设计的东西?
由于很多人对这个问题感兴趣,我会分享一下我的当前方法,希望能对某些人有所帮助。可能最好的方法是TH允许在GHC命令行上读取-D
参数,但目前似乎还没有实现这样的功能。
一个简单的模块可以使TH读取编译时环境。一个辅助函数也可以读取文件;例如从环境中读取配置文件的路径,然后读取该文件。
{-# LANGUAGE TemplateHaskell #-}
module THEnv
(
-- * Compile-time configuration
lookupCompileEnv
, lookupCompileEnvExp
, getCompileEnv
, getCompileEnvExp
, fileAsString
) where
import Control.Monad
import qualified Data.Text as T
import qualified Data.Text.IO as T
import Language.Haskell.TH
import Language.Haskell.TH.Syntax (Lift(..))
import System.Environment (getEnvironment)
-- Functions that work with compile-time configuration
-- | Looks up a compile-time environment variable.
lookupCompileEnv :: String -> Q (Maybe String)
lookupCompileEnv key = lookup key `liftM` runIO getEnvironment
-- | Looks up a compile-time environment variable. The result is a TH
-- expression of type @Maybe String@.
lookupCompileEnvExp :: String -> Q Exp
lookupCompileEnvExp = (`sigE` [t| Maybe String |]) . lift <=< lookupCompileEnv
-- We need to explicly type the result so that things like `print Nothing`
-- work.
-- | Looks up an compile-time environment variable and fail, if it's not
-- present.
getCompileEnv :: String -> Q String
getCompileEnv key =
lookupCompileEnv key >>=
maybe (fail $ "Environment variable " ++ key ++ " not defined") return
-- | Looks up an compile-time environment variable and fail, if it's not
-- present. The result is a TH expression of type @String@.
getCompileEnvExp :: String -> Q Exp
getCompileEnvExp = lift <=< getCompileEnv
-- | Loads the content of a file as a string constant expression.
-- The given path is relative to the source directory.
fileAsString :: FilePath -> Q Exp
fileAsString = do
-- addDependentFile path -- works only with template-haskell >= 2.7
stringE . T.unpack . T.strip <=< runIO . T.readFile
它可以这样使用:
{-# LANGUAGE TemplateHaskell #-}
import THEnv
main = print $( lookupCompileEnvExp "DEBUG" )
接着:
runhaskell Main.hs
命令将会输出 Nothing
;DEBUG="yes" runhaskell Main.hs
命令将会输出 Just "yes"
。
addDependentFile
将该文件注册到ghc --make
中是一个明显的替代方案。你对当前方案有什么问题? - Mikhail GlushenkovaddDependentFile
对我的情况很有帮助。当前方案正在运作,我只是想知道是否有其他规范的方法来做到这一点。 - Petrlocation
函数来获取项目目录的根目录(假设您知道从当前模块到根目录的相对路径)。这里是一个示例。 - Mikhail Glushenkov