两者都是类型是所有类型(无人居住)的交集的术语。两者都可以在代码中传递而不会失败,直到尝试评估它们。我能看到的唯一区别是,在 Java 中,有一个漏洞允许 null
仅针对一个操作进行评估,即引用相等比较 (==
)- - 而在 Haskell undefined
中,根本无法在不引发异常的情况下进行评估。这是唯一的区别吗?
编辑
我真正想解决的问题是,为什么在 Java 中包含 null
是一个如此糟糕的决定,以及 Haskell 如何逃避它?在我看来,真正的问题是你可以用null
做一些有用 的事情,即你可以检查它的nullness。因为您可以这样做,所以在代码中传递空值并让它们指示“无结果”而不是“此程序中存在逻辑错误”已成为标准约定。而在 Haskell 中,没有办法检查一个术语是否在不评估它的情况下评估到底部并且程序爆炸,所以它永远不能以这种方式使用来表示“没有结果”。相反,人们被迫使用类似 Maybe
的东西。
抱歉,如果我在使用“评估”这个词时似乎玩得太快了……我试图在这里做一个类比,但在准确表达它时遇到了麻烦。我想这表明类比不准确。
最佳答案
What's the difference between undefined in Haskell and null in Java?
好的,让我们后退一点。
Haskell 中的“未定义”是“底部”值的示例(表示为 ⊥)。这样的值表示程序中的任何未定义、卡住或部分状态。
存在许多不同形式的底部:非终止循环、异常、模式匹配失败——基本上是程序中在某种意义上未定义的任何状态。 undefined::a
值是将程序置于未定义状态的典型值示例。
undefined
本身并不是特别特别——它没有内置——你可以使用任何底部生成表达式来实现 Haskell 的 undefined
。例如。这是 undefined
的有效实现:
> undefined = undefined
或立即退出(旧的 Gofer 编译器使用此定义):
> undefined | False = undefined
bottom 的主要属性是,如果一个表达式的计算结果为底部,您的整个程序将计算为底部:程序处于未定义状态。
您为什么想要这样的值?好吧,在惰性语言中,您通常可以操作存储底部值的结构或函数,而程序本身并不处于底部。
例如无限循环列表是完美的:
> let xs = [ let f = f in f
, let g n = g (n+1) in g 0
]
> :t xs
xs :: [t]
> length xs
2
我对列表中的元素无能为力:
> head xs
^CInterrupted.
这种对无限事物的操纵是 Haskell 如此有趣和富有表现力的部分原因。懒惰的结果是 Haskell 特别关注 bottom
值。
但是,很明显,底部的概念同样适用于 Java 或任何(非全部)语言。在 Java 中,有许多表达式会产生“底部”值:
- 将引用与 null 进行比较(注意,不是
null
本身,它是明确定义的); - 除以零;
- 越界异常;
- 无限循环等
您只是没有能力很容易地用一个底部替换另一个底部,而且 Java 编译器并没有做很多事情来推理底部值。然而,这样的值(value)观是存在的。
总之,
- 在 Java 中取消引用
null
值是一种在 Java 中产生底部值的特定表达式; - Haskell 中的
undefined
值是一个通用的底部生成表达式,可以在 Haskell 中需要底部值的任何地方使用。
这就是它们的相似之处。
后记
关于null
本身的问题:为什么会被认为是不好的形式?
- 首先,Java 的
null
本质上相当于在 Haskell 中为每个类型a
添加一个隐式Maybe a
. - 取消引用
null
相当于仅对Just
情况进行模式匹配:f (Just a) = ... a ...
所以当传入的值是 Nothing
(在 Haskell 中)或 null
(在 Java 中)时,您的程序会进入未定义状态。这很糟糕:你的程序崩溃了。
因此,通过将 null
添加到 every 类型,您可以更轻松地意外创建 bottom
值——类型不再帮助你。您的语言不再帮助您防止这种特定类型的错误,这很糟糕。
当然,其他底部值仍然存在:异常(如 undefined
)或无限循环。为每个函数添加一个新的可能的失败模式——取消引用 null
——只会让编写崩溃的程序变得更容易。
关于java - Haskell 中的 undefined 和 Java 中的 null 有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3962939/