3

我刚开始学习 Template 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)

我在第一个变体中缺少什么?

4

1 回答 1

5

你试过tup用作拼接,但tup只是一个普通的值。您不想在它前面加上$.

此外,正如编译错误所说,由于 Template Haskell 在编译过程中运行,GHC 在完成当前模块编译之前确实需要知道它在做什么。这意味着您的拼接表达式不能依赖于tup,因为它仍在编译中。在拼接内部,您只能使用文字、导入的值以及特殊的'name''TypeName形式(我想您可以将其视为一种文字)。你可以通过使用 eg 从这个编译中获得一些信息reify,但即使这样也只能给你在编译时可用的数据——如果你想要一个函数,你可以将用户输入或从用户输入构造的数据传递给,那就是只是不可能。

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

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"))
于 2013-01-08T22:55:57.430 回答