Haskell 中的 undefined 和 Java 中的 null 有什么区别?
好的,让我们稍微备份一下。
Haskell 中的“未定义”是“底部”值的一个示例(表示为 ⊥)。这样的值表示程序中的任何未定义、卡住或部分状态。
存在许多不同形式的底部:非终止循环、异常、模式匹配失败——基本上是程序中在某种意义上未定义的任何状态。该值undefined :: a
是将程序置于未定义状态的值的规范示例。
undefined
它本身并不是特别特别——它没有undefined
内置——你可以使用任何底部收益率表达式来实现 Haskell。例如,这是一个有效的实现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 编译器并没有做很多关于底部值的推理。然而,这样的价值观是存在的。
总之,
- 在 Java 中取消引用一个
null
值是一种在 Java 中产生底部值的特定表达式;
- Haskell 中的
undefined
值是一个通用的底部生成表达式,可以在 Haskell 中需要底部值的任何地方使用。
这就是它们的相似之处。
后记
至于null
它本身的问题:为什么它被认为是不好的形式?
- 首先,Java
null
本质上相当于在 Haskell 中为每种类型添加一个隐式Maybe a
a
。
- 取消引用
null
仅在这种Just
情况下等同于模式匹配:f (Just a) = ... a ...
因此,当传入的值是Nothing
(在 Haskell 中)或null
(在 Java 中)时,您的程序会达到未定义状态。这很糟糕:您的程序崩溃了。
因此,通过添加null
到每种类型,您可以更轻松地bottom
意外创建值 - 类型不再帮助您。您的语言不再帮助您防止这种特定类型的错误,这很糟糕。
当然,其他底部值仍然存在:异常(如undefined
)或无限循环。为每个函数添加一个新的可能的故障模式——取消引用null
——只会让编写崩溃的程序变得更容易。