在Haskell中实现按值调用lambda演算

在Haskell中实现按值调用lambda演算,haskell,interpreter,lambda-calculus,Haskell,Interpreter,Lambda Calculus,在Haskell中实现按值调用lambda演算时,我是否应该强制对对象语言中的函数(即按值调用lambda演算)的参数求值,以绕过元语言(即Haskell)的按需调用求值顺序 具体来说,对于以下使用高阶抽象语法的实现: data Exp = Abs (Exp -> Exp) | App Exp Exp eval :: Exp -> Exp eval exp = case exp of Abs _ -> exp App opr opd -> c

在Haskell中实现按值调用lambda演算时,我是否应该强制对对象语言中的函数(即按值调用lambda演算)的参数求值,以绕过元语言(即Haskell)的按需调用求值顺序

具体来说,对于以下使用高阶抽象语法的实现:

data Exp
  = Abs (Exp -> Exp)
  | App Exp Exp

eval :: Exp -> Exp
eval exp = case exp of
  Abs _       -> exp
  App opr opd -> case eval opr of
    Abs fun -> eval (fun $ eval opd)  -- argument evaluation
在带有注释的行中,我是否应该使用
fun$强制评估
eval opd
!是否改为评估opd

我知道通过CPS转换可以避免对象和元级之间的求值顺序依赖。但我暂时不想打扰你。我只是想 确保在Haskell中忠实地实现了按值调用。我提出这个问题是因为我看到的许多示例实现似乎没有考虑到这一点。我是说
这些实现并不强制
评估opd
。我不知道是不是他们忽视了,还是我考虑太多了。谢谢。

使用
$eval e
求值为弱头范式对应于将
e
求值为目标语言中的一个值,code>就在这里起作用

让我演示一下,通过向
Exp
数据类型添加一个新构造函数来表示发散计算,以及一个我们可以实际打印的值:

data Exp
  = Abs (Exp -> Exp)
  | App Exp Exp
  | Bot
  | Value

printExp :: Exp -> String
printExp Bot = "bottom"
printExp Value = "base value"
printExp (Abs _) = "function"
printExp (App _ _) = "thunk"
为了简单起见,让我们将其转换为对求值函数中的
error
的调用

eval :: Exp -> Exp
eval exp = case exp of
  Bot         -> error "Evaluation would not terminate "
  Value       -> Value
  Abs _       -> exp
  App opr opd -> case eval opr of
    Abs fun -> eval (fun $ eval opd)
现在我们可以看到这是否是按值调用。下面的代码应该会出现分歧,因为我们正在将底部传递给函数。唉,它没有:

*Main> let e = App (Abs (\_ -> Value)) Bot
*Main> printExp e
"thunk"
*Main> printExp (eval e)
"base value"
$
更改为
$是否有帮助?是的,它有:

*Main> let e = App (Abs (const Value)) Bot
*Main> printExp (eval e)
"*** Exception: Evaluation would not terminate 
但这有多可靠?必须仔细检查
Exp
数据类型的任何更改,以确定强制最外层构造函数是否是您想要的,例如引入对


使用
deepseq
可能更可靠一些,即
$。但我真正建议的是添加一个
isValue::Exp->Bool
谓词,显式检查参数是否是目标语言的基值,并在调用
fun
之前在
eval
中检查
isValue(eval opd)
,谢谢您的回答和建议。所以我担心的事情确实存在,大多数人只是忽视了它。多么不幸啊!你是否真的有参考文献,人们依靠解释器中的
seq
来获取按值调用语义?似乎你误解了我的意思。我注意到人们在展示(预期的)按值调用lambda演算的示例实现时没有使用它。因为他们既不使用它,也不使用你建议的
isValue
谓词,我认为他们的实现不可靠。@plmday-我不是理论家,但我相信如果你不使用HOAS,你就不需要
seq
来获得CBV语义。因为您使用的是HOAS(以及Haskell的应用程序语义),所以您不得不处理“强制”。我认为即使我们不使用HOA,我们仍然需要对论点进行武力评估。假设我们使用闭包,应用程序的情况必须是:
App opr opd->case eval opr denv of{Clo var bod senv->eval bod$extend var(eval opd denv)senv}
,其中求值在得到求值之前已经到达函数体。