【问题标题】:Difference between call-by-value and call-by-name interpreter for the lambda calculuslambda演算的按值调用和按名称调用解释器之间的区别
【发布时间】:2015-05-01 15:31:25
【问题描述】:

在另一个问题中,Bob 提出了以下interpreter for the untyped lambda calculus

data Expr = Var String | Lam String Expr | App Expr Expr

data Value a = V a | F (Value a -> Value a)

interpret :: [(String, Value a)] -> Expr -> Value a
interpret env (Var x) = case lookup x env of
  Nothing -> error "undefined variable"
  Just v -> v
interpret env (Lam x e) = F (\v -> interpret ((x, v):env) e)
interpret env (App e1 e2) = case interpret env e1 of
  V _ -> error "not a function"
  F f -> f (interpret env e2)

Ivan Zakharyaschev remarked 由于F f -> f (interpret env e2),此解释器是按值调用的。 名称调用解释器的实现与上面介绍的解释器有何不同?

Plotkin 在 1970 年代研究了 call-by-name and call-by-value strategies 以评估 lambda 演算。

【问题讨论】:

  • 这对我来说看起来像是按需调用,因为 Haskell 是按需调用(模pedantry)
  • f $! interpret env e2 会让您按价值致电。
  • @loqui 这绝对不是按需调用或按名称调用。正常的按名称调用 Y 组合器会导致 infinite loop,这表明这实际上是按值调用并且需要按值调用 fixed-point combinator

标签: haskell lambda-calculus callbyname call-by-value


【解决方案1】:

我认为原始数据定义不可能进行正确的按名称调用。 F (Value a -> Value a)Value a 作为参数,所以我们别无选择,只能传入一些已经解释过的值,这将在 Haskell 减少行为下按需调用。

我们可以修改数据定义:

data Value a = V a | F ((() -> Value a) -> Value a)

并且还让解释器返回显式的 thunk:

interpret :: [(String, () -> Value a)] -> Expr -> () -> Value a
interpret env (Var x) = delay (case lookup x env of
  Nothing -> error "undefined variable"
  Just v -> force v)
interpret env (Lam x e) = delay (F (\v -> force (interpret ((x, v):env) e)))
interpret env (App e1 e2) = delay (case force (interpret env e1) of
  V _ -> error "not a function"
  F f -> f (interpret env e2))

force :: (() -> a) -> a
force f = f ()
{-# NOINLINE force #-}

delay :: a -> () -> a
delay a () = a
{-# NOINLINE delay #-}

现在,我们不是在环境中存储一个 thunk,而是存储一个 partial application object,然后在不同的调用站点分别评估它。

forcedelay 是防止 GHC 浮出函数体所必需的,thereby recovering sharing. 或者,可以使用 {-# OPTIONS -fno-full-laziness #-} 编译并使用简单的 lambdas 和应用程序来代替上述机制。

【讨论】:

  • 不幸的是,GHC 非常依赖完全惰性来支持其他转换。也许有一天有人会为此做点什么,但我并不乐观。
【解决方案2】:

CBV/CBN 是与 lambda 演算的评估策略相关的概念,即与 lambda term reduction 中的 redex 选择有关。在减少术语表示的操作式解释器中,您可以正确地说 CBV/CBN。

在像所发布的那样的指称式解释器中,我会说急切与懒惰的语义,而不是 CBV/CBN。当然,eager 对应 CBV,lazy 对应 CBN。

由于 Haskell 是懒惰的,代码

interpret env (App e1 e2) = case interpret env e1 of
  V _ -> error "not a function"
  F f -> f (interpret env e2)

实现了惰性语义 (CBN)。 (正如 luqui 所说,从操作上讲,GHC 会以按需调用的方式减少这种情况)。

为了获得一个 Eager (CBV) 语义,我们可以在调用之前强制参数:

interpret env (App e1 e2) = case interpret env e1 of
  V _ -> error "not a function"
  F f -> case interpret env e2 of
         V v -> f v
         F g -> f g

这可确保不会将未评估的 thunk 馈送到函数中,除非它们已经在环境中。然而,只有在评估 lambda 时才会填充环境,这将在环境中插入上面的值 v,g。因此,不会在此处插入任何 thunk。

【讨论】:

    猜你喜欢
    • 2011-09-04
    • 2014-08-10
    • 2013-06-29
    • 1970-01-01
    • 2015-06-07
    • 1970-01-01
    • 2013-10-02
    • 2017-02-07
    • 1970-01-01
    相关资源
    最近更新 更多