在Haskell中实现按值调用lambda演算
在Haskell中实现按值调用lambda演算时,我是否应该强制对对象语言中的函数(即按值调用lambda演算)的参数求值,以绕过元语言(即Haskell)的按需调用求值顺序 具体来说,对于以下使用高阶抽象语法的实现:在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
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}
,其中求值在得到求值之前已经到达函数体。