如何正确地将编译时信息传达给Template Haskell函数?


70

我需要将一些信息从编译脚本传达到Template Haskell中。当前,编译脚本将信息保存在系统环境中,因此我只是使用System.Environment.getEnvironment包装在中来阅读它runIO。是否有更好的方法,例如将一些参数传递给ghc(类似于-D...C预处理器),或者也许是TH中为此目的专门设计的?


9
从外部文件读取该信息并addDependentFile用于使该文件众所周知ghc --make是一个明显的选择。您目前的方案有什么问题?
2013年

2
@MikhailGlushenkov实际上,环境仅通过项目目录的根,然后从文件中读取更多信息。因此addDependentFile对我的案子会有帮助。当前的方案正在工作,我只是想知道是否还有其他一些规范的方法。
彼得

5
您还可以使用该location函数获取项目目录的根目录(假设您知道从当前模块到根目录的相对路径)。这是一个例子
2013年

您可以将-XCPP与模板haskell一起使用,但是看来您的方法效果更好。
aavogt

1
您是否希望某人向用户选择自己的配置文件,例如通过在命令行中传递文件路径?
user3125280 2013年

Answers:


14

由于有这么多人对此问题感兴趣,因此我将添加当前的方法,也许有人会觉得它有用。最好的方法可能是如果TH允许-D在GHC的命令行上读取参数,但是似乎目前没有实现。

一个简单的模块允许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"

3

看起来您要在这里做什么,ghc中的-D选项似乎定义了一个编译时间变量。

这里,关于同一主题的问题似乎也回答了您问题的另一部分。据我所知,要进行条件编译,您需要执行以下操作:

    #ifdef MACRO_NAME
    //Do stuff here
    #endif

1
正如我在评论中所说,我不想使用CPP和条件编译。我没有用,我只想将信息传递给Template Haskell。-D如果有一种方法可以在不使用CPP的情况下在TH中读取它,那么该选项将非常不错。
Petr 2013年

1
同样,这是为了在haskell代码中进行条件编译。除了使用-D(可能设置了一些值)来定义宏之外,我什么都不知道,然后您可以检查haskell中的值,它可能会起作用。我对Haskell的了解不足以确保。
violet_white 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.