Haskell 中的 undefined 和 Java 中的 null 有什么区别?
两者的类型都是所有类型的交集(无人居住)。两者都可以在代码中传递而不会失败,直到尝试评估它们为止。我能看到的唯一区别是,在 Java 中,有一个漏洞允许对一个操作评估 null
,即引用相等比较 (==
) - - 而在 Haskell 中,在不引发异常的情况下根本无法对 undefined
进行求值。这是唯一的区别吗?
编辑
我真正想解决这个问题的是,为什么在 Java 中包含 null
是一个明显糟糕的决定,Haskell 如何逃避它?在我看来,真正的问题是您可以使用 null
做一些有用的事情,即您可以检查它的nullness。因为您可以这样做,所以在代码中传递空值并让它们指示“无结果”而不是“此程序中存在逻辑错误”已成为标准约定。而在 Haskell 中,没有办法在不评估术语和程序爆炸的情况下检查术语是否评估为底部,因此它永远不能以这种方式使用来指示“无结果”。相反,人们被迫使用诸如“也许”之类的东西。
抱歉,如果我似乎对“评估”这个词玩得太快了……我试图在这里做一个类比,但很难准确地表达它。我想这表明这个类比不准确。
Both are terms whose type is the intersection of all types (uninhabited). Both can be passed around in code without failing until one attempts to evaluate them. The only difference I can see is that in Java, there is a loophole which allows null
to be evaluated for exactly one operation, which is reference equality comparison (==
)--whereas in Haskell undefined
can't be evaluated at all without throwing an exception. Is this the only difference?
Edit
What I'm really trying to get at with this question is, why was including null
in Java such an apparently poor decision, and how does Haskell escape it? It seems to me that the real problem is that you can do something useful with null
, namely you can check it for nullness. Because you are allowed to do this, it has become standard convention to pass around null values in code and have them indicate "no result" instead of "there is a logical error in this program". Whereas in Haskell, there's no way to check if a term evaluates to bottom without evaluating it and the program exploding, so it could never be used in such a way to indicate "no result". Instead, one is forced to use something like Maybe
.
Sorry if it seems like I'm playing fast and loose with the term "evaluate"... I'm trying to draw an analogy here and having trouble phrasing it precisely. I guess that's a sign that the analogy is imprecise.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
好吧,让我们稍微备份一下。
Haskell 中的“未定义”是“底部”值(表示为 ⊥)的一个示例。这样的值表示程序中任何未定义、卡住或部分状态。
存在许多不同形式的底层:非终止循环、异常、模式匹配失败——基本上是程序中在某种意义上未定义的任何状态。值
undefined :: a
是将程序置于未定义状态的典型值示例。undefined
本身并不是特别特殊——它没有连接——并且您可以使用任何底部产生表达式来实现 Haskell 的undefined
。例如,这是undefined
的有效实现:或立即退出(旧的 Gofer 编译器使用此定义):
bottom 的主要属性是,如果表达式求值为bottom,则整个程序将求值为bottom :程序处于未定义状态。
为什么你想要这样的值?好吧,在惰性语言中,您通常可以操作存储底部值的结构或函数,而程序本身并不位于底部。
例如,无限循环列表是完美的:
我只是不能对列表的元素做太多事情:
这种对无限东西的操作是 Haskell 如此有趣和富有表现力的部分原因。懒惰的结果是 Haskell 特别关注
bottom
值。然而,显然,底层的概念同样适用于 Java 或任何(非总体)语言。在 Java 中,有许多表达式会产生“底部”值:
null
本身,这是明确定义的);您只是无法轻松地将一个底部替换为另一个底部,并且 Java 编译器不会对底部值进行太多推理 然而,这样的价值观是存在的。
总之,
null
值是一种在 Java 中产生底部值的特定表达式;undefined
值是一个通用的底部产生表达式,可以在 Haskell 中需要底部值的任何地方使用。这就是他们的相似之处。
Postscript
至于
null
本身的问题:为什么它被认为是不好的形式?null
相当于仅适用于Just
情况的模式匹配:f (Just a) = ... a ...
因此,当传入的值是
Nothing
(在 Haskell 中)或null
(在 Java 中),您的程序将达到未定义状态。这很糟糕:你的程序崩溃了。因此,通过将
null
添加到每个类型,您就可以更轻松地意外创建bottom
值 - 这些类型不再帮助你。您的语言不再帮助您防止这种特定类型的错误,这很糟糕。当然,其他底部值仍然存在:异常(例如
undefined
)或无限循环。为每个函数添加一个新的可能的故障模式——取消引用null
——只会让编写崩溃的程序变得更容易。Ok, let's back up a little.
"undefined" in Haskell is an example of a "bottom" value (denoted ⊥). Such a value represents any undefined, stuck or partial state in the program.
Many different forms of bottom exist: non-terminating loops, exceptions, pattern match failures -- basically any state in the program that is undefined in some sense. The value
undefined :: a
is a canonical example of a value that puts the program in an undefined state.undefined
itself isn't particularly special -- its not wired in -- and you can implement Haskell'sundefined
using any bottom-yielding expression. E.g. this is a valid implementation ofundefined
:Or exiting immediately (the old Gofer compiler used this definition):
The primary property of bottom is that if an expression evaluates to bottom, your entire program will evaluate to bottom: the program is in an undefined state.
Why would you want such a value? Well, in a lazy language, you can often manipulate structures or functions that store bottom values, without the program being itself bottom.
E.g. a list of infinite loops is perfectly cromulent:
I just can't do much with the elements of the list:
This manipulation of infinite stuff is part of why Haskell's so fun and expressive. A result of laziness is Haskell pays particularly close attention to
bottom
values.However, clearly, the concept of bottom applies equally well to Java, or any (non-total) language. In Java, there are many expressions that yield "bottom" values:
null
itself, which is well-defined);You just don't have the ability to substitute one bottom for another very easily, and the Java compiler doesn't do a lot to reason about bottom values. However, such values are there.
In summary,
null
value in Java is one specific expression that yields a bottom value in Java;undefined
value in Haskell is a generic bottom-yielding expression that can be used anywhere a bottom value is required in Haskell.That's how they're similar.
Postscript
As to the question of
null
itself: why it is considered bad form?null
is essentially equivalent to adding an implicitMaybe a
to every typea
in Haskell.null
is equivalent to pattern matching for only theJust
case:f (Just a) = ... a ...
So when the value passed in is
Nothing
(in Haskell), ornull
(in Java), your program reaches an undefined state. This is bad: your program crashes.So, by adding
null
to every type, you've just made it far easier to createbottom
values by accident -- the types no longer help you. Your language is no longer helping you prevent that particular kind of error, and that's bad.Of course, other bottom values are still there: exceptions (like
undefined
) , or infinite loops. Adding a new possible failure mode to every function -- dereferencingnull
-- just makes it easier to write programs that crash.你的描述不太正确。你是说
null
无法被评估。然而,由于 java 是一种急切的语言,这意味着无论f
的定义是什么,f(null)
都会抛出一个 NPE(因为方法参数总是在之前评估)该方法运行)。您可以在 haskell 中传递
undefined
而不会出现异常的唯一原因是 haskell 很懒,除非需要,否则不会评估参数。undefined 和 null 之间的另一个区别是
undefined
是标准库中定义的一个简单值。如果标准库中没有定义它,您可以自己定义它(例如,通过编写myUndefine = error "My Undefined
)。在 Java 中,
null
是一个关键字。如果没有null
关键字,您将无法定义它(执行与 haskell 定义相同的操作,即Object myNull = throw(new Exception())
,不起作用,因为表达式将在那里被评估)。Your description isn't quite correct. You're saying
null
can't be evaluated. However since java is an eager language, this would mean thatf(null)
would throw an NPE no matter what the definition off
is (because method arguments are always evaluated before the method runs).The only reason that you can pass around
undefined
in haskell without getting an exception is that haskell is lazy and does not evaluate arguments unless needed.One further difference between undefined and null is that
undefined
is a simple value defined in the standard library. If it weren't defined in the standard library you could define it yourself (by writingmyUndefined = error "My Undefined
for example).In Java
null
is a keyword. If there were nonull
keyword, you wouldn't be able to define it (doing the equivalent of the haskell definition, i.e.Object myNull = throw(new Exception())
, wouldn't work because the expression would be evaluated right there).