模板Haskell阶段限制的麻烦

时间:2013-01-08 20:40:11

标签: haskell template-haskell

我刚开始学习模板Haskell,并坚持使用拼接的简单问题 在一个模块中,我实现了函数tupleN,它回复了元组的第N个元素:

tupleN :: Lift a => a -> Int -> Q Exp
tupleN a n = do
   (TupE as) <- lift a
   return $ as !! n

在主模块中我有:

main :: IO ()
main = do
   let tup = (1::Int,'a',"hello")
   putStrLn $ show $(tupleN $tup 1)

这似乎有效,但事实并非如此。编译器打印错误:

GHC stage restriction: `tup'
  is used in a top-level splice or annotation,
  and must be imported, not defined locally
In the expression: tup
In the first argument of `tupleN', namely `$tup'
In the expression: tupleN ($tup) 1

如果我将元组描述放入拼接表达式,代码就会起作用:

main :: IO ()
main = do
   putStrLn $ show $(tupleN (1::Int,'a',"hello") 1)

第一个变种我错过了什么?

1 个答案:

答案 0 :(得分:5)

您已尝试将tup用作拼接,但tup只是一个普通值。您不希望在其前面添加$

此外,正如编译错误所述,由于模板Haskell在编译过程中运行,GHC确实需要知道它在完成编译当前模块之前所做的事情。这意味着您的拼接表达式不能依赖tup,因为它仍在编译中。在拼接内部,您只能使用文字,导入的值以及特殊的'name''TypeName形式(您可以将其视为一种文字,我想)。您可以使用以下方法获取此编译中的一些信息: reify,但即便如此,只能为您提供编译时可用的数据 - 如果您想要一个可以传递用户输入的函数,或者是根据用户输入构建的数据,那么这只是不可能的。

简而言之,您无法使用Template Haskell完成您想要做的事情。但是,您可以定义一个扩展为函数的拼接,以获取大小为i的元组的sz th 元素:

import Control.Monad (unless)
import Language.Haskell.TH

tupleN :: Int -> Int -> Q Exp
tupleN sz i = do
  unless (i < sz) . reportError $ "tupleN: index " ++ show i
    ++ " out of bounds for " ++ show sz ++ "-tuple"
  lamE
    [tupP (replicate i wildP
      ++ [varP (mkName "x")]
      ++ replicate (sz - i - 1) wildP)]
    (varE (mkName "x"))