一个quasiquoter'使用'变量可以在它被调用的同一个文件中定义吗?

时间:2013-09-20 09:18:50

标签: haskell template-haskell

所以,我开始尝试使用quasiquotation和模板haskell。

我想修改现有的(大)准规范代码,同时使用在“被调用”的地方定义的变量的实际值。举一个简单的例子说明:

main.hs

{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}

import Language.Haskell.TH
import Exp02

x = "cde"

main = do
  putStrLn [str|$x|]

Exp02.hs

{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}

module Exp02 where

import Language.Haskell.TH
import Language.Haskell.TH.Syntax
import Language.Haskell.TH.Quote

xpto :: String -> ExpQ
xpto [] = stringE []
xpto ('$':rest) = varE (mkName rest)
xpto str = stringE str

str = QuasiQuoter
  { quoteExp = xpto
  , quotePat = fail $ "patterns"
  , quoteType= fail $ "types"
  , quoteDec = fail $ "declarations"
  }

虽然这会编译并打印出“cde”,但这不是我想要的。我的理解是拼接后的主要代码是:putStrLn x。我想要的是生成putStrLn cde(我知道这不是有效的haskell代码,但它只是代表我的观点)。

因此,换句话说,我不想'在主文件中创建对变量x的引用',我想在xpto quasiquoter中实际使用它的值代码。

我猜这可能是不可能的,因为它意味着main.hsExp02.hs之间的循环引用,因此面临TH阶段限制。这是正确的,还是有办法在xpto代码中使用x

谢谢!

1 个答案:

答案 0 :(得分:6)

不,你目前要做的事情是不可能的。来自template haskell docs

  

如果从另一个模块导入函数,则只能在编译时运行函数,该模块不是包含当前正在编译的模块的相互递归的模块组的一部分。此外,必须通过从要运行拼接的模块的非SOURCE导入来访问相互递归组的所有模块。

     

例如,在编译模块A时,如果B不直接或间接导入A,则只能运行从B导入的模板Haskell函数。原因应该是明确的:运行B我们必须编译并运行A,但我们目前正在对A进行类型检查。

您正在尝试在编译时在x定义的同一模块中运行该函数(或更严格的值)x,这显然是不允许的。