首页 > 解决方案 > Typed Tagless Final Interpreters: what is the use of duplicate?

问题描述

This question is related to the paper Typed Tagless Final Interpreters. In page 11, a function trice is presented, which relies on a duplicate function:

enter image description here

I've tried coding this into Haskell, and the resulting functions look as follows:

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

However, since I cannot seem to be able to give a more generic type to thrice I could have just written the simpler function:

thrice' :: (Int, (String, Tree)) -> IO ()
thrice' (reprInt, (reprStr, reprTree)) = do
    print $ eval reprInt
    print $ view reprStr
    print $ toTree reprTree    

So I was wondering what is the use of duplicate in this example?

标签: haskellinterpreter

解决方案


首先,顺便说一句,请注意那篇文章中的代码已经是有效的 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"]]]
> 

这些最终解释器的想法是打字决定解释。解释器的目的只是添加没有显式类型签名的类型信息。因此,eval (neg (lit 1))可以在没有类型签名的 GHCi 提示符下输入:

> eval (neg (lit 1))
-1
>

它“起作用”是因为eval——这只是id函数——强制返回值是一个整数,这反过来又会选择正确的实例来评估最终表达式,而不是viewing 它或其他东西。但你也可以写:

> neg (lit 1) :: Int
-1
>

以获得相同的效果。

事实证明,这duplicate比其他解释器更不需要,因为在唯一使用它的地方——即定义dup_consume

dup_consume ev x = print (ev x1) >> return x2
  where (x1, x2) = duplicate x

类型检查器已经可以推断出需要一个元组,因此为 提供的任何最终表达式x,likeneg (lit 1)必然会被解释为元组的重复实例(即,在定义之前定义的实例duplicate),所以 - 如上所述-- 你可以写:

dup_consume ev x = print (ev x1) >> return x2
  where (x1, x2) = x

类型检查器会弄清楚的。


推荐阅读