Dmi*_*lov 3 haskell template-haskell
我刚开始学习模板Haskell,并坚持使用拼接的简单问题.
在一个模块中,我实现了tupleN回复元组的第N个元素的函数:
tupleN :: Lift a => a -> Int -> Q Exp
tupleN a n = do
(TupE as) <- lift a
return $ as !! n
Run Code Online (Sandbox Code Playgroud)
在Main模块中,我有:
main :: IO ()
main = do
let tup = (1::Int,'a',"hello")
putStrLn $ show $(tupleN $tup 1)
Run Code Online (Sandbox Code Playgroud)
这似乎有效,但事实并非如此.编译器打印错误:
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
Run Code Online (Sandbox Code Playgroud)
如果我将元组描述正确放入拼接表达式,代码就会起作用:
main :: IO ()
main = do
putStrLn $ show $(tupleN (1::Int,'a',"hello") 1)
Run Code Online (Sandbox Code Playgroud)
第一个变种我错过了什么?
你试图tup用作拼接,但tup它只是一个普通的值.你不想用它作为前缀$.
此外,正如编译错误所述,由于模板Haskell在编译过程中运行,GHC在完成编译当前模块之前确实需要知道它正在做什么.这意味着你的splice表达式不能依赖tup,因为它仍在编译中.在拼接内部,你只能使用文字,导入的值,以及特殊'name和''TypeName形式(我认为你可以将其视为一种文字).您可以通过使用eg来获取此编译中的一些信息reify,但即使这样也只能为您提供在编译时可用的数据 - 如果您想要一个可以传递用户输入的函数,或者是从用户输入构造的数据,那么只是不可能.
简而言之,您无法使用Template Haskell完成您想要做的事情.但是,您可以定义一个扩展为函数的拼接,以获取大小元组的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"))
Run Code Online (Sandbox Code Playgroud)