【问题标题】:How to properly communicate compile-time information to Template Haskell functions?如何正确地将编译时信息传递给 Template Haskell 函数?
【发布时间】:2013-11-09 20:03:50
【问题描述】:

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

【问题讨论】:

  • 从外部文件读取该信息并使用addDependentFile 使ghc --make 知道该文件是一个明显的替代方案。您当前的方案存在哪些问题?
  • @MikhailGlushenkov 实际上,环境仅传递项目目录的根目录,然后从文件中读取更多信息。所以addDependentFile 将对我的情况有所帮助。目前的方案正在运行,我只是想知道是否有其他规范的方法来做到这一点。
  • 也可以使用location函数获取项目目录的根目录(假设你知道当前模块到根目录的相对路径)。这是an example
  • 您可以将 -XCPP 与模板 haskell 一起使用,但您的方式似乎效果更好。
  • 您是否希望用户选择自己的配置文件,例如通过在命令行传递文件路径?

标签: haskell compile-time template-haskell


【解决方案1】:

既然这么多人对这个问题感兴趣,我会添加我目前的方法,也许有人会觉得它有用。如果 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"

【讨论】:

    【解决方案2】:

    看起来你正在尝试做什么here,ghc 中的 -D 选项似乎定义了一个编译时变量。

    在同一主题上,question 似乎也回答了您问题的另一部分。 据我所知,要进行条件编译,您可以执行以下操作:

        #ifdef MACRO_NAME
        //Do stuff here
        #endif
    

    【讨论】:

    • 正如我在cmets中所说的,我不想使用CPP和条件编译。我没有用它,我只想将信息传递给 Template Haskell。 -D 选项会很好,如果有一种方法可以在没有 CPP 的情况下在 TH 中读取它。
    • 同样,这是用于 haskell 代码中的条件编译。除了用 -D 定义宏(可能设置了一些值)之外,我什么都不知道,然后你可以检查你的 haskell 中的值,它可能会起作用。不过,我对 haskell 的了解还不够,无法确定。
    猜你喜欢
    • 2019-11-05
    • 1970-01-01
    • 2021-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-14
    • 2019-08-24
    • 1970-01-01
    相关资源
    最近更新 更多