为什么这个模板Haskell有效?

时间:2012-03-06 17:00:01

标签: haskell template-haskell

考虑以下代码:

magic :: String -> Q Exp
magic s = [e| putStrLn s |]

现在,尽我所知,这实际上不应该起作用。在牛津括号内,s不在范围内。然而,上述情况显然完美无缺。

如果我们稍微改变这个例子,它现在可怕地破坏了:

magic :: Exp -> Q Exp
magic (VarE n) = [e| putStrLn (nameBase n) |]

就像之前一样,我们有一个不在范围内的变量。并且这个时间,它打破了。但它没有抱怨不在范围内的变量;相反,它抱怨一些没有实例的无证件类。<​​/ p>

任何人都知道到底发生了什么事?

2 个答案:

答案 0 :(得分:12)

牛津括号内的

s 。基本上,您可以在引用的表达式中使用多种类型的值 - 具有Lift个实例的值 - 并且它们将自动转换为适当的代码以在另一端重新创建相应的值。

例如,Lift instance for Integers只构造相应的整数文字,而instance for Maybe只构造相应的构造函数应用程序。您甚至可以定义自己的Lift

实例

您收到“无实例”错误,因为nName,而Lift无效。

答案 1 :(得分:2)

我认为简短的回答是魔术函数预期会起作用,因为引号括号会捕获它们的局部变量(在某种程度上)。本质上,引号括号中的编译时局部变量将替换为其文字值,并成为运行时常量。这是通过隐式调用lift函数来实现的,这样[| .. var .. |]变为[| $(lift var)|]。

也许您会混淆这种行为,因为它们唯一地捕获局部变量,因此重复调用相同的引号不会干扰彼此的变量名。这是通过在幕后调用newName来实现的,这确保了唯一的变量名称。

如果它有帮助,我个人认为引号括号为“拼接生成器” - Haskell代码的一小部分将在编译时转换为AST,因此将成为随时可插入的拼接。正如Bulat的教程(links from)所指出的那样,它们就像一种宏预处理器,因为它们是Haskell函数的混合,生成代码并将haskell代码简单自动转换为TH AST。

编辑:似乎ehird打败了我的答案 - 我将离开我的答案,以防它提供一些额外的价值。