模板Haskell:GHC阶段限制以及如何克服

me2*_*me2 7 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 []]
    where
        nam = name obj
        pat = varP (mkName $ "find" ++ nam)
        bod = normalB [| findName nam |]
Run Code Online (Sandbox Code Playgroud)

然后我在主文件中有以下内容:

{-# 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"
Run Code Online (Sandbox Code Playgroud)

我想为我$(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
Run Code Online (Sandbox Code Playgroud)

我该如何克服这个问题?我宁可不要定义one,two等在一个单独的文件.

第二个问题是为什么$(findx (Alpha "Four" 4))没有问题?

Ben*_*Ben 6

我自己并不是很熟悉模板Haskell,但基于我有限的理解,问题是threeGHC在尝试编译时在某种意义上"仍然被定义" $(findx 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代码,然后重新编译所有内容.缺点(除了涉及的工作)将使得更复杂地说明你可以传递给Template Haskell的确切限制.