6

我正在阅读有关 Haskell 指称语义(http://en.wikibooks.org/wiki/Haskell/Denotational_semantics)的内容,但我不明白为什么在一个类型中,与“正常”相比,底部“值”被置于另一个级别值,例如为什么它不能被模式匹配。

我相信模式修补底部会造成麻烦,因为底部也表示非终止计算,但为什么非终止计算和错误应该被同等对待?(我假设使用不受支持的参数调用部分函数可以被视为错误)。

如果所有 Haskell 类型都包含一个模式匹配的 Java-null-like 值而不是底部,那么会丢失哪些有用的属性?

换句话说:为什么不明智地通过提升所有类型的空值来使所有 Haskell 函数总计?

(非终止计算是否需要特殊类型?)

4

2 回答 2

17

在不限制语言的图灵完整性的情况下,您无法摆脱不终止,并且由于停止问题,我们通常无法检测到不终止并将其替换为值。

所以每一种图灵完备语言都有底。

Haskell 和 Java 之间的唯一区别是 Java 有底部空值。Haskell 没有后者,这很方便,因为这样我们就不必检查空值了!

换句话说,既然底部是不可避免的(在图灵完备的世界中),那么除了招致 bug 之外,让所有东西都可以为空还有什么意义呢?

还要注意,虽然 Prelude 中的一些函数由于历史原因是部分的,但现代 Haskell 风格倾向于在几乎所有地方编写总函数,并Maybe在函数中使用显式返回类型,head否则会是部分的。

于 2013-02-03T21:01:30.893 回答
14

我在评论中的狡辩无法忍受,我认为 sclv 回答了你问题的第一部分,但至于

如果所有 Haskell 类型都包含一个模式匹配的 Java-null-like 值而不是底部,那么会丢失哪些有用的属性?

换句话说:为什么不明智地通过提升所有类型的空值来使所有 Haskell 函数总计?

在这里,您似乎在区分非终止和异常。所以,虽然不可能(由于停止问题)在非终止时进行模式匹配,但为什么不能在异常时进行模式匹配呢?

我用我自己的一个问题来回答这个问题:那些从不抛出异常的函数呢?Haskell 毕竟具有全部功能。如果已知某些东西是非异常的,我不应该使用模式匹配来确保它是非异常的。Haskell 作为一种束缚和纪律语言,自然会想在类型上传达这种差异。也许通过写作

Integer

对于已知不是异常的整数类型和

?Integer

对于可能是异常的整数类型。答案是我们已经这样做了:Haskell 在前奏中有一个类型

data Maybe a = Just a | Nothing

可以理解为“要么有,a要么什么都没有”。我们可以进行模式匹配,Maybe所以这个提议不会给我们任何东西。(我们也有类型Either更丰富的“可能出错的计算”以及花哨的 monad 语法/组合器,以使这些易于使用)。

那么,为什么会有例外呢?在 Haskell 中,除了 IO monad 之外,我们不能“捕获”异常。如果我们可以完美地模拟异常MaybeEither为什么语言中有异常?

对此有几个答案,但核心是Haskell 异常是不精确的。可能会出现异常,因为您的程序内存不足,或者您正在执行的线程被另一个线程杀死,或者一大堆其他不可预测的原因。此外,通常对于异常,我们关心我们得到哪个异常。那么下面的表达式会导致什么结果呢?

(error "error 1") + (error "error 2") :: Integer

这个表达式显然应该导致异常,但是哪个异常呢? (+)专门针对 Integer 的两个参数都很严格,所以这无济于事。我们可以决定它是第一个值,但一般来说我们会有

x + y =/= y + x

这将限制我们对等式推理的选择。Haskell 提供了一种行为不精确的异常概念,这很重要,因为语言的纯部分具有完全精确的行为,并且可能会受到限制。

于 2013-02-04T02:46:43.050 回答