Haskell中的undefined和Java中的null有什么区别?
两者都是术语的types是所有types的交集(无人居住)。 两者都可以在代码中传递,而不会失败,直到试图评估它们。 我可以看到的唯一区别是,在Java中,有一个漏洞,允许null
被评估为一个操作,这是参考相等比较( ==
) – 而在哈斯克尔undefined
根本不能被评估没有投掷一个例外。 这是唯一的区别吗?
编辑
我真正想要解决这个问题的是,为什么在Java中包含null
这样一个显然很差的决定,Haskell如何逃避它呢? 在我看来,真正的问题是你可以用null
做一些有用的事情,即你可以检查它是否为null 。 因为您可以这样做,所以在代码中传递null值并使它们指示“无结果”而不是“此程序中存在逻辑错误”已成为标准惯例。 而在Haskell中,没有办法检查一个术语是否评估到底而没有对它进行评估,程序是否爆炸,所以它永远不能用来表示“没有结果”。 相反,我们不得不使用像Maybe
这样的东西。
对不起,如果看起来我玩的是“评估”这个词,那么我就玩得很快,而且我也试图在这里作一个比喻,并且无法精确地expression出来。 我想这是一个类比不精确的迹象。
Haskell中的undefined和Java中的null有什么区别?
好的,让我们稍微回顾一下。
Haskell中的“undefined”是“bottom”值的示例(表示为⊥)。 这样的值表示程序中的任何未定义的,卡住的或部分的状态。
存在许多不同forms的底层:非终止循环,exception,模式匹配失败 – 基本上,程序中的任何状态在某种意义上都是未定义的。 值undefined :: a
是将程序置于未定义状态的值的典型示例。
undefined
本身不是特别特殊 – 它没有连接 – 你可以使用任何底部产生的expression式来实现Haskell的undefined
。 例如,这是一个undefined
的有效实现:
> undefined = undefined
或者立即退出(旧的Gofer编译器使用这个定义):
> undefined | False = undefined
底部的主要属性是,如果expression式评估为最低,则整个程序将评估到最低:程序处于未定义状态。
你为什么要这样的价值? 那么,用一种懒惰的语言,你可以经常操纵那些存储底层值的结构或函数,而程序本身并不是底层的。
例如一个无限循环的列表是完全有力的:
> let xs = [ let f = f in f , let gn = g (n+1) in g 0 ] > :t xs xs :: [t] > length xs 2
我不能用列表中的元素做很多事情:
> head xs ^CInterrupted.
无限的东西的操纵是为什么Haskell如此有趣和performance力的一部分。 Haskell对懒惰的结果是特别关注bottom
价值。
但是,显然,底部的概念同样适用于Java或任何(非全部)语言。 在Java中,有许多expression式产生“底部”值:
- 比较引用与空(虽然注意,不是
null
本身,这是很好的定义); - 被零除;
- 越界例外;
- 无限循环等
你只是没有能力很容易地将一个底部replace成另一个底部,而Java编译器对于底部值的理由并没有太多的理由。 但是,这样的价值在那里。
综上所述,
- 在Java中取消引用
null
值是一个在Java中产生最低值的特定expression式; - Haskell中的
undefined
值是一个通用的底部产生式expression式,可以在Haskell需要的底部值的任何地方使用。
这就是他们的相似之处。
后记
至于null
本身的问题:为什么它被认为是不好的forms?
- 首先,Java的
null
本质上等同于在Haskell中为每个typesa
添加隐式Maybe a
。 - 取消引用
null
相当于仅用于case的模式匹配:f (Just a) = ... a ...
所以当传入的值是Nothing
(在Haskell中)或null
(在Java中)时,程序会达到一个未定义的状态。 这是不好的:你的程序崩溃了。
所以,通过为每种types添加null
,您可以轻而易举地创buildbottom
值 – 这些types不再有助于您。 你的语言不再帮助你防止那种特殊的错误,那很糟糕。
当然,其他底部值仍然存在:exception( undefined
)或无限循环。 为每个函数添加一个新的可能的失败模式 – 解引用null
– 只是使编写崩溃的程序更容易。
您的描述不完全正确。 你说null
不能被评估。 然而,由于java是一种渴望的语言,这意味着无论f(null)
的定义是什么(因为方法参数总是在方法运行之前被评估f(null)
, f(null)
会抛出一个NPE。
你可以在haskell中传递undefined
的唯一原因是haskell是惰性的,除非需要,否则不会计算参数。
undefined和null之间的另一个区别是undefined
是在标准库中定义的简单值。 如果没有在标准库中定义,你可以自己定义(通过编写myUndefined = error "My Undefined
)。
在Java中null
是一个关键字。 如果没有null
关键字,你将无法定义它(相当于haskell定义,即Object myNull = throw(new Exception())
将不起作用,因为expression式将在那里被评估) 。