lambda演算的call-by-value和call-by-name解释器之间的区别

时间:2015-03-01 04:40:34

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

在另一个问题中,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在20世纪70年代研究call-by-name and call-by-value strategies评估lambda演算。

2 个答案:

答案 0 :(得分:5)

我不认为使用原始数据定义可以正确调用名称。 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 #-}编译并使用简单的lambda和应用程序而不是上述机器。

答案 1 :(得分:3)

CBV / CBN是与lambda演算的评估策略相关的概念,即与λ术语缩减中的redex的选择有关。在减少术语表示的操作风格的解释器中,您可以恰当地谈论CBV / CBN。

在像发布者这样的指称式解释器中,我说的是渴望与懒惰的语义,而不是CBV / CBN。当然,渴望对应于CBV,而对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会按照需要的方式减少这种情况。)

要获得急切(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时填充环境,该lambda将在环境中插入值vg。因此,那里不会插入任何thunk。