键入无标签最终口译员:重复的用途是什么?

时间:2018-07-21 15:31:29

标签: haskell interpreter

这个问题与论文Typed Tagless Final Interpreters有关。在第11页中,介绍了一个函数trice,该函数依赖于duplicate函数:

enter image description here

我尝试将其编码为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的用途是什么?

2 个答案:

答案 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中。

但是,文章中的最终“解释器” 全部没有真正的目的。这就是重点。特别是,您也不需要evalview来定义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,因为解析结果可以保持多态并以不同的方式解释。