Haskell 中的未定义和 Java 中的 null 有什么区别?
好吧,让我们备份一下。
Haskell中的“undefined”是“底部”值的一个例子(⊥表示)。此类值表示程序中任何未定义、卡住或部分状态。
存在许多不同形式的底层:非终止循环,异常,模式匹配失败 - 基本上是程序中在某种意义上未定义的任何状态。该值是将程序置于未定义状态的值的规范示例。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.
这种对无限事物的操纵是哈斯克尔如此有趣和富有表现力的部分原因。懒惰的结果是哈斯克尔特别关注价值观。bottom
然而,显然,底部的概念同样适用于Java或任何(非全部)语言。在Java中,有许多表达式可以产生“底部”值:
- 将引用与 null 进行比较(虽然注意,但不是它本身,这是明确定义的);
null
- 除以零;
- 越界异常;
- 无限循环等
你只是没有能力很容易地用一个底部代替另一个底部,Java编译器也没有做很多事情来推理底部值。但是,这些值是存在的。
综上所述,
- 在Java中取消引用值是一个特定的表达式,在Java中产生一个底部值;
null
- Haskell 中的值是一个通用的底部生成表达式,可以在 Haskell 中需要底部值的任何地方使用。
undefined
这就是它们的相似之处。
附言
至于它本身的问题:为什么它被认为是不好的形式?null
- 首先,Java本质上等同于在Haskell中的每个类型a中添加一个隐式
的Maybe
a
。null
- 仅对于以下情况,取消引用等效于模式匹配:
null
Just
f (Just a) = ... a ...
因此,当传入的值是(在Haskell中)或(在Java中)时,您的程序将达到未定义的状态。这很糟糕:你的程序崩溃了。Nothing
null
所以,通过添加到每一种类型中,你只是让偶然创造价值变得更加容易 - 这些类型不再帮助你。你的语言不再能帮助你防止这种特定的错误,这很糟糕。null
bottom
当然,其他底部值仍然存在:异常(如)或无限循环。向每个函数添加新的可能的故障模式 (取消引用) 只是使编写崩溃的程序变得更加容易。undefined
null