这两个术语的类型是所有类型(无人居住)的交集。两者都可以在代码中传递而不会失败,直到有人试图评估它们。我能看到的唯一区别是,在Java中,存在一个漏洞,它允许null
仅针对一个操作进行评估,即参考相等性比较(==
) - 而在Haskell中{{1如果不抛出异常,就无法进行评估。这是唯一的区别吗?
我真正想要解决的问题是,为什么在Java中包含undefined
这样一个显然很糟糕的决定,以及Haskell如何逃脱它?在我看来,真正的问题是你可以用null
做一些有用的,即你可以检查 nullness 。因为您可以这样做,所以在代码中传递空值并使它们表示“无结果”而不是“此程序中存在逻辑错误”已成为标准惯例。而在Haskell中,没有办法检查一个术语是否在没有评估它且程序爆炸的情况下评估到底部,因此它永远不会用这种方式来表示“没有结果”。相反,人们不得不使用像null
这样的东西。
对不起,如果看起来我用“评价”这个词快速而松散...我试图在这里进行类比,并且难以准确地说出它。我想这表明这个比喻是不精确的。
答案 0 :(得分:72)
Haskell中的undefined与Java中的null有什么区别?
好的,让我们稍微备份一下。
Haskell中的“未定义”是“底部”值(表示为⊥)的示例。这样的值表示程序中任何未定义,卡住或部分状态。存在许多不同形式的底层:非终止循环,异常,模式匹配失败 - 基本上程序中的任何状态在某种意义上都是未定义的。值undefined :: a
是将程序置于未定义状态的值的规范示例。
undefined
本身并不特别 - 它没有连接 - 你可以使用任何底部产生的表达式实现Haskell的undefined
。例如。这是undefined
的有效实现:
> undefined = undefined
或立即退出(旧Gofer编译器使用此定义):
> undefined | False = undefined
bottom的主要属性是,如果表达式求值为bottom,则整个程序将评估为bottom:程序处于未定义状态。
为什么你想要这样的价值?好吧,在一种懒惰的语言中,你经常可以操作存储底部值的结构或函数,而程序本身并不是最低的。
E.g。无限循环的列表完全是cromulent:
> 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
本身,这是明确定义的); 你只是没有能力很容易地将一个底部替换为另一个底部,而Java编译器对底部值的推理没有太大作用。但是,有这样的价值观。
总之,
null
值是一个在Java中产生底值的特定表达式; undefined
值是一个通用的底部产生表达式,可以在Haskell中需要底值的任何地方使用。这就是他们的相似之处。
<强>后记强>
关于null
本身的问题:为什么它被视为不良形式?
null
基本上等同于向Haskell中的每个类型Maybe a
添加隐式a
。null
仅相当于Just
案例的模式匹配:f (Just a) = ... a ...
因此,当传入的值为Nothing
(在Haskell中)或null
(在Java中)时,程序将达到未定义状态。这很糟糕:你的程序崩溃了。
因此,通过将null
添加到每个类型的,您只是更容易意外地创建bottom
值 - 这些类型不再对您有所帮助。您的语言不再帮助您防止出现这种特殊错误,而且这种情况很糟糕。
当然,其他底部值仍然存在:异常(如undefined
)或无限循环。为每个函数添加一个新的可能失败模式 - 解除引用null
- 只是更容易编写崩溃的程序。
答案 1 :(得分:7)
您的描述不太正确。你说null
无法评估。但是,由于java是一种急切的语言,这意味着无论f(null)
的定义是什么,f
都会抛出NPE(因为方法参数总是在方法运行之前进行评估)。
你可以在没有异常的情况下在haskell中传递undefined
的唯一原因是haskell是懒惰的,除非需要,否则不会评估参数。
undefined和null之间的另一个区别是undefined
是标准库中定义的简单值。如果它没有在标准库中定义,您可以自己定义它(例如,通过编写myUndefined = error "My Undefined
)。
在Java null
中是一个关键字。如果没有null
关键字,您将无法定义它(执行相当于haskell定义,即Object myNull = throw(new Exception())
,将无效,因为表达式将在那里进行评估)。