我正在努力熟悉Template Haskell,令我惊讶的是,下面的代码在ghc
(版本6.10.4)下编译。
main = do let y = [| "hello" + 1 |] putStr ""
这告诉我,准引号内没有类型检查。这不是我在阅读Template Haskell上的原始paper后所期望的。此外,以下程序无法编译。
main = do let y = [| "hello" && True |] putStr ""
这里发生了什么?
答案 0 :(得分:15)
看起来GHC确实键入了所有引用,但假设可以满足所有生成的实例约束。
在此代码中:
main = do
let
y = [| "hello" + 1 |]
putStr ""
假设我们有一个Num String实例,y括号是可输入的。由于GHC无法确定在拼接y之前不会引入这样的实例,因此不会出现类型错误。
在此代码中:
main = do
let
y = [| "hello" && True |]
putStr ""
无论您设置何种实例环境,都无法成功拼接y。
这只是模板Haskell的类型检查机制过于宽松的一个例子 - 另外的例子在Simon PJ在http://hackage.haskell.org/trac/ghc/blog/Template%20Haskell%20Proposal的博客文章中进行了讨论,在那里他提出了一个改变,即不打算检查任何引用。< / p>
答案 1 :(得分:12)
模板Haskell有两个主要操作:
[| |]
$( )
当你在牛津括号中包装东西时,你会延迟它的类型检查(和评估),而是构建一个AST片段,当它被拼接回来时将进行类型检查。
可以观察到构建的AST:
{-# LANGUAGE TemplateHaskell #-}
import Language.Haskell.TH
main = print =<< runQ [| "hello" + 1 |]
运行这个程序(或将括号表达式输入到GHCi中),我们得到一个结构良好的AST,但如果被视为Haskell片段,那么它的类型不正确:
InfixE(Just(LitE(StringL“hello”)))(VarE GHC.Num。+)(Just(LitE(IntegerL 1)))
现在,当我们尝试拼接它时,会发生类型检查:
*Main> :t [| "hello" + 1 |]
[| "hello" + 1 |] :: Q Exp
*Main> $( [| "hello" + 1 |] )
<interactive>:1:4:
No instance for (Num [Char])
arising from the literal `1'
正如我们所料。所以,是的,TH表达式是类型检查的,但是在最后一点,当拼接回程序时。