ExO*_*ter 15 haskell runtime-error case lazy-evaluation
我目前正在学习 Haskell 并遇到了这个表达式。
\nstatement\n case 1 \xc2\xb4div\xc2\xb4 0 of _ -> 42\nRun Code Online (Sandbox Code Playgroud)\n我的直觉是,由于除以 0,这将导致运行时错误,但从测试来看,情况并非如此。
\n我的结论是,这一定是由于 Haskell 内部的惰性求值造成的。因为_任何东西的匹配它都不会检查它与什么比较?
因此,是否有人可以告诉我这个评估是否正确,如果不正确,原因是什么。另请详细说明在不实际查看表达式的情况下 case 行可以匹配的前提。
\nCar*_*arl 15
你的分析很准确,但是这实际上很繁琐。这并不复杂,但需要仔细考虑非常精确的问题。
case 表达式具有一般形式
case exp0 of {pat1 -> exp1 ; ...}
Run Code Online (Sandbox Code Playgroud)
当 case 表达式被求值时,exp0被求值得足够远以确定它是否匹配pat1。如果是这样,则 case 表达式将使用为其创建的exp1任何附加绑定进行计算。pat1如果不匹配,则会进入下一个模式或防护并重复。(如果没有匹配,则计算结果为异常。)
值得注意的是,您的第一个替代方案可以匹配所有内容,无需任何评估。div 1 0因此不进行任何评估。该模式不绑定任何内容,然后 case 表达式的计算结果为 42。
这与您所做的不同:
case 1 `div` 0 of
0 -> 42
_ -> 42
Run Code Online (Sandbox Code Playgroud)
在这种情况下,div必须对表达式求值以查看它是否等于 0。这会导致异常,该异常将成为整个 case 表达式的结果。即使两个分支具有相同的值也是如此。模式的内容对于确定与严格性相关的行为非常重要。
GHC Haskell 编译器是迄今为止最常见的,它有一个称为“核心”的中间表示,它看起来与 Haskell 类似,但类型类和大多数语法糖完全脱糖。Core 的语义也与 Haskell 略有不同,特别是在其 case 结构方面。在核心中,case 总是会导致对顶级构造函数的求值。通过嵌套重写模式以匹配原始 Haskell 代码的模式语义。
本节有点旁白,但如果您研究优化 Haskell 代码,您将遇到核心,并且了解其中的差异很有用。
chi*_*chi 10
我的结论是,这一定是由于 Haskell 内部的惰性求值造成的。因为
_任何东西的匹配它都不会检查它与什么比较?
对,就是这样!
在模式匹配期间,仅执行匹配模式中的构造函数(和数字/字符文字)所需的评估。如果模式是变量或通配符_,则不需要计算。
case undefined of _ -> () -- OK
case undefined of True -> () -- crash
case undefined of (_,_) -> () -- crash
case (undefined, 4) of (_,x) -> x+1 -- OK
Run Code Online (Sandbox Code Playgroud)
此外,模式匹配是从左到右、从上到下进行的:
case (4, undefined) of
(3, True) -> 1
(4, _) -> 2
Run Code Online (Sandbox Code Playgroud)
评估为2- 我们首先匹配4,但在我们尝试匹配3之前失败。通过这种方式,我们避免了崩溃,第二种模式成功了。undefinedTrue(4, _)
(详尽地说:实际上有一个例外。如果您针对newtype构造函数进行模式匹配,则不会发生任何评估。您可以暂时忽略这种情况,因为您正在学习 - 这并不常见。)