Haskell 中的 undefined 和 Java 中的 null 有什么区别?

发布于 2024-09-27 20:16:40 字数 581 浏览 1 评论 0原文

两者的类型都是所有类型的交集(无人居住)。两者都可以在代码中传递而不会失败,直到尝试评估它们为止。我能看到的唯一区别是,在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

花开半夏魅人心 2024-10-04 20:16:40

Haskell 中的 undefined 和 Java 中的 null 有什么区别?

好吧,让我们稍微备份一下。

Haskell 中的“未定义”是“底部”值(表示为 ⊥)的一个示例。这样的值表示程序中任何未定义、卡住或部分状态。

存在许多不同形式的底层:非终止循环、异常、模式匹配失败——基本上是程序中在某种意义上未定义的任何状态。值undefined :: a 是将程序置于未定义状态的典型值示例。

undefined 本身并不是特别特殊——它没有连接——并且您可以使用任何底部产生表达式来实现 Haskell 的 undefined 。例如,这是 undefined 的有效实现:

 > undefined = undefined

或立即退出(旧的 Gofer 编译器使用此定义):

 > undefined | False = undefined

bottom 的主要属性是,如果表达式求值为bottom,则整个程序将求值为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 编译器不会对底部值进行太多推理 然而,这样的价值观是存在的。

总之,

  • 在 Java 中取消引用 null 值是一种在 Java 中产生底部值的特定表达式;
  • Haskell 中的 undefined 值是一个通用的底部产生表达式,可以在 Haskell 中需要底部值的任何地方使用。

这就是他们的相似之处。

Postscript

至于null本身的问题:为什么它被认为是不好的形式?

  • 首先,Java 的 null 本质上相当于为 Haskell 中的每个类型添加一个隐式的 Maybe a 。
  • 取消引用 null 相当于仅适用于 Just 情况的模式匹配:f (Just a) = ... a ...

因此,当传入的值是 Nothing (在 Haskell 中)或 null (在 Java 中),您的程序将达到未定义状态。这很糟糕:你的程序崩溃了。

因此,通过将 null 添加到每个类型,您就可以更轻松地意外创建 bottom 值 - 这些类型不再帮助你。您的语言不再帮助您防止这种特定类型的错误,这很糟糕。

当然,其他底部值仍然存在:异常(例如 undefined)或无限循环。为每个函数添加一个新的可能的故障模式——取消引用null——只会让编写崩溃的程序变得更容易。

What's the difference between undefined in Haskell and null in Java?

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's undefined using any bottom-yielding expression. E.g. this is a valid implementation of undefined:

 > undefined = undefined

Or exiting immediately (the old Gofer compiler used this definition):

 > undefined | False = undefined

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:

 > let xs = [ let f = f in f 
            , let g n = g (n+1) in g 0
            ]
 > :t xs
 xs :: [t]
 > length xs
 2

I just can't do much with the elements of the list:

 > head xs
 ^CInterrupted.

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:

  • comparing a reference against null (though note, not null itself, which is well-defined);
  • division by zero;
  • out-of-bounds exceptions;
  • an infinite loop, etc.

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,

  • dereferencing a null value in Java is one specific expression that yields a bottom value in Java;
  • the 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?

  • Firstly, Java's null is essentially equivalent to adding an implicit Maybe a to every type a in Haskell.
  • Dereferencing null is equivalent to pattern matching for only the Just case: f (Just a) = ... a ...

So when the value passed in is Nothing (in Haskell), or null (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 create bottom 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 -- dereferencing null -- just makes it easier to write programs that crash.

扮仙女 2024-10-04 20:16:40

你的描述不太正确。你是说 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 that f(null) would throw an NPE no matter what the definition of f 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 writing myUndefined = error "My Undefined for example).

In Java null is a keyword. If there were no null 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).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文