模板Haskell:GHC阶段限制及克服方法
我在模块中有以下代码:模板Haskell:GHC阶段限制及克服方法,haskell,template-haskell,Haskell,Template Haskell,我在模块中有以下代码: {-# LANGUAGE TemplateHaskell #-} module Alpha where import Language.Haskell.TH import Data.List data Alpha = Alpha { name :: String, value :: Int } deriving (Show) findName n = find ((== n) . name) findx obj = sequence [valD pat bod [
{-# LANGUAGE TemplateHaskell #-}
module Alpha where
import Language.Haskell.TH
import Data.List
data Alpha = Alpha { name :: String, value :: Int } deriving (Show)
findName n = find ((== n) . name)
findx obj = sequence [valD pat bod []]
where
nam = name obj
pat = varP (mkName $ "find" ++ nam)
bod = normalB [| findName nam |]
然后我在主文件中有以下内容:
{-# LANGUAGE TemplateHaskell #-}
import Alpha
one = Alpha "One" 1
two = Alpha "Two" 2
three = Alpha "Three" 3
xs = [one, two , three]
findOne = findName "One"
findTwo = findName "Two"
$(findx three) -- This Fails
$(findx (Alpha "Four" 4)) -- This Works
main = putStrLn "Done"
我希望$(findx-three)
为我创建findThree=findName“three”
。但是,我得到了这个错误:
GHC stage restriction: `three'
is used in a top-level splice or annotation,
and must be imported, not defined locally
In the first argument of `findx', namely `three'
In the expression: findx three
我如何克服这个问题?我宁愿不必在一个单独的文件中定义一个、两个等
第二个问题是为什么
$(findx(Alpha“Four”4))
工作没有问题?我自己也不是很了解Haskell,但基于我有限的理解,问题是当GHC试图编译$(findx three)
时,three在某种意义上“仍在定义中”,而$(findx(Alpha“Four”4))
的所有组件都已完全定义
基本问题是同一模块中的所有定义相互影响。这是由于类型推断以及相互递归。定义x=[]
可能意味着许多不同的东西,具体取决于上下文;它可以将x
绑定到Int
列表,或IO()
列表,或任何其他内容。GHC可能必须对整个模块进行处理,以准确了解它的含义(或它实际上是一个错误)
该分析必须考虑模板Haskell发送到正在编译的模块中的代码。这意味着模板Haskell代码必须在GHC理解模块中的定义之前运行,因此从逻辑上讲,您不能使用任何定义
当GHC编译该模块时,已经对从其他模块OTOH导入的内容进行了全面检查。通过编译本模块,无需了解更多关于它们的信息。因此,在编译本模块中的代码之前,可以访问和使用这些内容
另一种思考方式:也许three
实际上不应该是Alpha
类型。也许这是一个输入错误,构造函数应该是Alphz
。通常,GHC通过编译模块中使用three
的所有其他代码来发现这些类型的错误,以查看这是否会导致不一致。但是,如果该代码使用或被仅由$(findx-three)
发出的东西使用,该怎么办?在我们运行它之前,我们甚至不知道它将是什么代码,但在我们运行它之前,我们无法解决three
是否正确键入的问题
当然,在某些情况下可以稍微取消这一限制(我不知道这是容易还是实际)。也许我们可以让GHC考虑一些“早期定义”,如果是导入的,或者它只使用其他“早期定义”的东西(也许还有一个明确的类型签名)。也许它可以尝试在不运行TH代码的情况下编译模块,如果它能够在遇到任何错误之前对three
进行完全的类型检查,那么它可以将其输入TH代码,然后重新编译所有内容。不利的一面(除了所涉及的工作)是,说明对传递给模板Haskell的内容有哪些确切限制会变得更加复杂。如果您只是尝试在定义的同一模块中引用名称,您可能会以某种方式通过引用/准引用来做到这一点?如果您试图在拼接中实际执行某些操作,请忘记它。编译器根本不能这么做。$(findx-three)
和$(findx(Alpha“Four”4))
之间有什么区别?前者实际上会导致错误,但后者会起作用。three
在同一模块中定义,而Alpha
则不是。对于路过的人,有趣的功能可以帮助解决某些舞台限制问题。另请参阅。我认为,对传递给模板Haskell的内容的限制实际上不会复杂得多,但编码可能非常困难。