我刚开始学习模板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)
第一个变种我错过了什么?
答案 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"))