这个问题与论文Typed Tagless Final Interpreters有关。在第11页中,介绍了一个函数trice
,该函数依赖于duplicate
函数:
我尝试将其编码为Haskell,结果函数如下:
thrice :: (Int, (String, Tree)) -> IO () -- Is this the most generic type we can give?
thrice x0 = do
x1 <- dupConsume eval x0
x2 <- dupConsume view x1
print $ toTree x2
where
dupConsume ev y = do
print (ev y0)
return y1
where
(y0, y1) = duplicate y
但是,由于我似乎无法为thrice
提供更通用的类型,因此我可以编写更简单的函数:
thrice' :: (Int, (String, Tree)) -> IO ()
thrice' (reprInt, (reprStr, reprTree)) = do
print $ eval reprInt
print $ view reprStr
print $ toTree reprTree
所以我想知道在此示例中duplicate
的用途是什么?
答案 0 :(得分:1)
首先,顺便说一句,请注意,该文章中的代码已经是有效的Haskell代码,只是使用了一些符号代替常用的Haskell语法。例如,符号“◦”代替(.)
运算符用于功能组合。
因此,您可以直接根据文章中的定义将thrice
编写为以下有效的Haskell代码:
thrice x = dup_consume eval x >>= dup_consume view
>>= print . toTree
dup_consume ev x = print (ev x1) >> return x2
where (x1, x2) = duplicate x
无论如何,回到您的问题...正如您正确指出的那样,解释器duplicate
没有真正的目的。例如,您可以将dup_consume
定义为以上版本,或者完全删除duplicate
并输入:
dup_consume ev x = print (ev x1) >> return x2
where (x1, x2) = x
当然,您可以像这样将dup_consume
的定义直接合并到thrice
中。
但是,文章中的最终“解释器” 全部没有真正的目的。这就是重点。特别是,您也不需要eval
或view
来定义thrice
。以下也可以正常工作:
thrice' :: (Int, (String, Tree)) -> IO ()
thrice' (reprInt, (reprStr, reprTree)) = do
print $ reprInt
print $ reprStr
print $ reprTree
之后,您可以执行以下操作:
> thrice' (add (lit 5) (neg (lit 2)))
3
"(5 + (-2))"
Node "Add" [Node "Lit" [Leaf "5"],Node "Neg" [Node "Lit" [Leaf "2"]]]
>
使用这些最终解释器的想法是,键入确定解释。解释器的目的只是添加没有显式类型签名的键入信息。因此,可以在GHCi提示符下输入eval (neg (lit 1))
,而无需输入类型签名:
> eval (neg (lit 1))
-1
>
之所以起作用,是因为eval
(仅是id
函数)将返回值强制为整数,这反过来又选择了正确的实例进行 evaluate < / em>最终表达式,而不是view
对其或其他内容进行编码。但您也可以这样写:
> neg (lit 1) :: Int
-1
>
获得相同的效果。
事实证明,duplicate
的必要性甚至比其他解释器还要少,因为在唯一使用它的地方,即dup_consume
的定义:
dup_consume ev x = print (ev x1) >> return x2
where (x1, x2) = duplicate x
类型检查器已经可以推断出需要一个元组,因此为x
提供的任何最终表达式(例如neg (lit 1)
)都将被解释为元组的复制实例(即,已定义的实例)就在duplicate
的定义之前,因此-如上所述-您可以编写:
dup_consume ev x = print (ev x1) >> return x2
where (x1, x2) = x
,类型检查器会找出来。
答案 1 :(得分:0)
我可能错了,但我怀疑您的 thrice'
函数可能涉及多次解析表达式,而 Oleg 的 duplicate
技巧只会复制解析树(ie parse结果)。
重复的需要源于解析结果的模式匹配,它为匹配的解析结果分配一个单态类型。因此,一旦你为它选择了一个口译员,你就会被它困住。该论文提到了一种更高等级的编码,以牺牲可扩展性为代价来回收这种丢失的多态性,这与无标签最终解释器的观点相悖。
另一种方法是 duplicate
技巧,它将(单态)解析结果复制到另一个(单态)值中以进行不同的解释。
当然,如果解析总是成功(例如通过直接在您的解析树中编码解析错误),那么就不需要 duplicate
,因为解析结果可以保持多态并以不同的方式解释。