41

我在一个编译器类,我们的任务是从头开始创建我们自己的语言。目前我们的困境是是否包含“null”类型。null 提供什么目的?我们团队中的一些人认为这不是绝对必要的,而其他人则支持 null 只是因为它可以提供额外的灵活性。

你有什么想法,特别是支持或反对null?您是否曾经创建过需要 null 的功能?

4

25 回答 25

47

空:十亿美元的错误。托尼·霍尔:

我称之为我的十亿美元错误。它是 1965 年空引用的发明。当时,我正在为面向对象语言 (ALGOL W) 中的引用设计第一个综合类型系统。我的目标是确保所有引用的使用都应该是绝对安全的,并由编译器自动执行检查。但我无法抗拒放入空引用的诱惑,仅仅是因为它很容易实现。这导致了无数的错误、漏洞和系统崩溃,在过去的四十年中可能造成了十亿美元的痛苦和损失。近年来,微软的一些程序分析器如 PREfix 和 PREfast 已被用于检查引用,并在存在可能为非空的风险时发出警告。 更新的编程语言如 Spec# 引入了非空引用的声明。这就是我在 1965 年拒绝的解决方案。

于 2009-02-25T02:31:54.910 回答
30

null是一个哨兵值,它不是整数,不是字符串,也不是布尔值 - 不是任何东西,除了要保存的东西并且是一个“不存在”的值。不要将其视为或期望它是 0、空字符串或空列表。这些都是有效值,并且在许多情况下都可以是真正有效的值——null 的想法反而意味着那里没有价值。

也许它有点像一个抛出异常而不是返回值的函数。除了不是制造并返回一个具有特殊含义的普通值,它返回一个已经具有特殊含义的特殊值。如果一种语言希望您使用null,那么您就不能真正忽略它。

于 2009-02-25T02:48:31.063 回答
27

哦不,我觉得哲学专业从我身上出来了......

NULL 的概念来自集合论中的空集的概念。几乎每个人都同意空集不等于零。几十年来,数学家和哲学家一直在争论集合论的价值。

在编程语言中,我认为理解不引用内存中任何内容的对象引用非常有帮助。谷歌一下集合论,你会发现集合论者使用的形式符号系统(符号)和我们在许多计算机语言中使用的符号之间的相似之处。

问候,山姆

于 2009-02-25T03:14:15.710 回答
14

你问什么是空的?

好,

没有。

于 2009-02-25T02:43:17.377 回答
12

我通常会在“内存地址 0”的 C/C++ 方面想到“null”。它不是严格需要的,但如果它不存在,那么人们只会使用其他东西(如果 myNumber == -1,或者如果 myString == "")。

我所知道的是,我想不出我在编码上没有输入“null”这个词的一天,所以我认为这非常重要。

在 .NET 世界中,MS 最近为 int、long 等添加了可空类型,这些类型过去从未可空,所以我猜他们认为它也非常重要。

如果我在设计一种语言,我会保留它。但是,我不会避免使用也没有 null 的语言。它也需要一点时间来适应。

于 2009-02-25T02:38:45.580 回答
7

零的概念不是严格必要的,就像零的概念不是严格必要的一样。

于 2009-02-25T02:56:32.103 回答
7

我认为在整个语言设计的上下文之外谈论 null 并没有什么帮助。混淆的第一点:null 类型是空的,还是包含一个单独的、可区分的值(通常称为“nil”)?完全为空的类型不是很有用——尽管 C 使用空返回类型void来标记仅出于副作用而执行的过程,但许多其他语言为此目的使用单例类型(通常是空元组)。

我发现在动态类型语言中使用 nil 值最有效。在 Smalltalk 中,它是当您需要一个值但您没有任何信息时使用的值。在 Lua 中,它的使用更加有效:nil 值是 Lua 表中唯一不能作为键或值的值。在 Lua 中,nil 也被用作缺失参数或结果的值。

总的来说,我会说 nil在动态类型设置中可能很有用,但在静态类型设置中,空类型仅用于讨论为副作用而执行的函数(或过程或方法)。

不惜一切代价避免在 C 和 Java 中使用NULL 指针。这些是指针和对象实现中固有的工件,在设计良好的语言中它们不应该被允许。无论如何,为您的用户提供一种使用空值扩展现有类型的方法,但要让他们明确地这样做——不要意外地强制每种类型都有一个。(作为显式使用的一个例子,我最近在 Haskell 中实现了 Bentley 和 Sedgewick 的三元搜索树,我需要用一个额外的值来扩展字符类型,意思是“不是字符”。为此,Haskell 提供了该Maybe类型。)

最后,如果您正在编写编译器,最好记住语言中最容易编译的部分以及导致最少错误的部分是不存在的部分:-)

于 2009-02-25T06:42:07.367 回答
5

有一种方法来指示当前未指向任何东西的引用或指针似乎很有用,无论您称其为 null、nil、None 等。如果没有其他原因让人们知道他们何时将要跌倒离开链表的末尾。

于 2009-02-25T02:36:24.133 回答
5

在 C 中,NULL 是 (void*(0)),所以它是一个带值 (?) 的类型。但这不适用于 C++ 模板,因此 C++ 将 NULL 设为 0,它删除了类型并成为纯值。

然而,发现具有特定的 NULL 类型会更好,因此他们(C++ 委员会)决定 NULL 将再次成为一种类型(在 C++0x 中)。

此外,除了 C++ 之外,几乎所有语言都具有 NULL 作为类型,或者与 0 不同的等效唯一值(它可能等于或不等于它,但它的值不同)。

所以现在即使 C++ 也会使用 NULL 作为类型,基本上结束了关于此事的讨论,因为现在每个人(几乎)都会有一个 NULL 类型

编辑:考虑一下 Haskell 可能是 NULL 类型的另一种解决方案,但它并不容易掌握或实现。

于 2009-02-25T04:15:24.440 回答
5

null 的一个实际示例是当您提出是/否问题但没有得到回应时。您不想默认为“否”,因为在答案非常重要的情况下,知道问题没有得到回答可能很重要。

于 2009-02-25T23:30:58.580 回答
3

您可以将任何类型视为一个集合以及一组操作。在许多情况下,使用不是“正常”值的值很方便;例如,考虑一个“EOF”值。对于 C 的getline()。您可以通过以下几种方式之一来处理它:您可以在集合之外有一个 NULL 值,您可以将特定值区分为 null(在 C 中,((void *)0)可以服务于该目的)或者您可以有一种创建新类型的方法,所以对于类型T,您创建一个类型T' =def { T ∪ NULL },这就是 Haskell 的做法(“可能”类型)。

哪一个更好对于很多有趣的争论都有好处。

于 2009-02-25T02:40:18.757 回答
3

例如,考虑 C 和 Java 的例子。在 C 中,约定是空指针是数值零。当然,这实际上只是一个约定:该语言没有将这个值视为任何特殊的东西。然而,在 Java 中,这null是一个独特的概念,您可以检测并知道,是的,这实际上是一个不好的参考,我不应该尝试打开那扇门来查看另一边的内容。

即便如此,我讨厌空值几乎比其他任何东西都糟糕。

基于评论的澄清null:我讨厌零的事实上的空指针值比我讨厌的更糟糕。

每当我看到对 null 的赋值时,我都会想,“哦,太好了,有人刚刚在代码中放了地雷。有一天,我们将沿着相关的执行路径走下去,然后BOOM!NullPointerException!”

我更希望有人指定一个有用的默认值或 NullObject,让我知道“此参数尚未设置为任何有用的值”。光秃秃的 null 本身就是等待发生的麻烦。

也就是说,它仍然比一个散漫的原始零要好。

于 2009-02-25T02:42:41.340 回答
3

Null 不是错误。Null 表示“我还不知道”

对于原语,您实际上并不需要 null (我不得不说字符串(在 .NET 中)不应该得到它恕我直言)

但是对于复合实体来说,它肯定是有目的的。

于 2009-02-25T02:43:01.263 回答
3

Null 仅在存在未分配值的变量的情况下有用。如果每个变量都有一个值,那么就不需要空值。

于 2009-02-25T02:48:53.637 回答
3

Null 是一个标记值。这是一个不可能是真实数据的值,而是提供有关正在使用的变量的元数据。

分配给指针的 N​​ull 表示指针未初始化。这使您能够通过检测空值指针的取消引用来检测未初始化指针的滥用。相反,如果您让指针的值等于内存中发生的任何值,那么您将拥有非常不规则的程序行为,这将更加难以调试。

此外,C 风格的可变长度字符串中的空字符用于标记字符串的结尾。

以这些方式使用 null,尤其是指针值,已经变得如此流行,以至于该隐喻已被导入其他系统,即使“null”标记值的实现方式完全不同并且与数字 0 无关。

于 2009-02-25T03:24:16.327 回答
3

Null 不是问题——每个人都以不同的方式对待和解释 null 是问题所在。

我喜欢空。如果没有空值,空值只会被其他方式替换,代码会说“我不知道,伙计!” (有些人会写“我不知道,伙计!”或“我不知道,老豆!”等等,所以我们又会遇到完全相同的问题)。

我概括,我知道。

于 2009-02-25T08:34:02.603 回答
2

该决定取决于编程语言的目标。

你在为谁设计编程语言?您是为熟悉 c 派生语言的人设计的吗?如果是这样,那么您可能应该添加对 null 的支持。

一般来说,我会说你应该避免违背人们的期望,除非它有特定的目的。

以 C# 中的 switch-blocks 为例。C# 中的所有 case 标签必须在每个分支中都有一个显式的控制流表达式。也就是说,它们都必须以“break”语句或显式 goto 结尾。这意味着虽然此代码是合法的:

switch(x)
{
    case 1:
    case 2:
        foo;
        break;
}

该代码不合法​​:

switch (x)
{
    case 1:
        foo();
    case 2:
        bar();
        break;
}

为了创建从案例 1 到案例 2 的“失败”,有必要插入一个 goto,如下所示:

switch (x)
{
    case 1:
        foo();
        goto case 2;
    case 2:
        bar();
        break;
}

这可以说是违背了 C++ 程序员的期望的事情,他们倾向于 C#。但是,添加该限制是有目的的。它消除了整个类常见 C++ 错误的可能性。它稍微增加了语言的学习曲线,但结果对程序员来说是一个净收益。

如果您的目标是设计一种针对 C++ 程序员的语言,那么删除 null 可能会违反他们的期望。这将导致混乱,并使您的语言更难学习。那么关键问题是,“他们得到什么好处”?或者,或者,“这会造成什么损害”。

如果你只是想设计一种可以在一个学期的课程中实现的“超级小语言”,那么情况就不同了。在这种情况下,您的目标不是构建针对特定人群的有用语言。相反,它只是为了学习如何创建编译器。在这种情况下,使用较小的语言是一个很大的好处,因此消除 null 是值得的。

所以,回顾一下,我想说你应该:

  1. 确定创建语言的目标。语言是为谁设计的,他们的需求是什么。
  2. 根据什么可以帮助目标用户以最佳方式实现目标来做出决定。

通常这将使期望的结果非常清楚。

当然,如果你没有明确表达你的设计目标,或者你不能就它们是什么达成一致,那么你仍然会争论。但是,在这种情况下,无论如何,您几乎注定要失败。

于 2009-02-25T05:35:54.543 回答
2

另一种看待 null 的方式是它是一个性能问题。如果您有一个包含其他复杂对象等的复杂对象,那么允许所有属性最初为空而不是创建某种无用并很快被替换的空对象会更有效。

这只是我以前看不到的一种观点。

于 2009-02-25T08:25:31.580 回答
2

null 提供什么目的?

我相信这里有两个 null 概念在起作用。

第一个(逻辑指示符为空)是一种传统的程序语言机制,它提供程序逻辑中未初始化内存引用的运行时指示。

第二个(null the value)是一个基本数据值,可以在逻辑表达式中用于检测逻辑空指示符(前面的定义)并在程序代码中做出逻辑决策。

你有什么想法,特别是支持或反对null?

虽然 null 多年来一直是许多程序员的祸根,也是许多应用程序故障的根源,但 null 的概念是有效的。如果您和您的团队创建的语言使用的内存引用可能会因为引用未初始化而被滥用,那么您可能需要一种机制来检测这种可能性。创建替代方案始终是一种选择,但 null 是一种广为人知的替代方案。

最重要的是,这完全取决于您的语言目标:

  1. 目标编程受众
  2. 稳健性
  3. 表现
  4. ETC...

如果您的优先级列表中的健壮性和程序正确性很高,并且您允许程序化内存引用,您将需要考虑 null。

BB

于 2009-02-25T13:52:43.510 回答
1

如果您正在创建一种静态类型的语言,我想 null 可能会给您的编译器增加很多复杂性。

如果您正在创建动态类型语言,NULL 可以派上用场,因为它只是另一种“类型”,没有任何变化。

于 2009-02-25T02:34:45.823 回答
1

Null 是一个占位符,这意味着不能为该变量分配任何值(对于静态类型语言附加“正确类型的”)。

这里存在认知失调。我在别处听说人类无法理解否定,因为他们必须先假设一个值,然后再想象它的不合适性。

于 2009-02-25T07:03:44.367 回答
1

我对你的团队的建议是:提出一些需要用你的语言编写的示例程序,看看如果你遗漏了它们会是什么样null子,如果你包含它。

于 2009-02-27T21:48:20.617 回答
1

使用空对象模式!

如果你的语言是面向对象的,让它有一个UndefinedValue只存在一个单例实例的类。然后在任何使用的地方使用这个实例null。这样做的好处是您null将响应诸如#toString和之类的消息#equals。您永远不会像在 Java 中那样遇到空指针异常。(当然,这要求您的语言是动态类型的)。

于 2009-03-01T19:36:38.267 回答
0

Null 为尚未完全考虑其程序所需的逻辑和域的程序员提供了一个简单的出路,或者使用一个基本上没有明确和一致定义的值的未来维护影响。

乍一看,它必须意味着“没有价值”似乎很明显,但实际上这意味着什么取决于上下文。如果,例如 LastName === null,这是否意味着该人没有姓氏,或者我们不知道他们的姓氏是什么,或者还没有输入系统?null 是否等于自身,或者不是吗?在 SQL 中它没有。在许多语言中它确实如此。但是如果我们不知道 personA.lastName 或 personB.lastName 的值,我们怎么知道 personA.lastName === personB.lastName,嗯?结果应该是假的,还是...... 空值?

这取决于你在做什么,这就是为什么拥有某种系统范围的值是危险和愚蠢的,它可以用于任何看起来像“无”的情况,因为你的程序的其他部分和不能真正依赖外部库或模块来正确解释“null”的含义。

你最好清楚地定义 lastName 的可能值的 DOMAIN,以及每个可能值的实际含义,而不是依赖于一些模糊的系统范围的 null 概念,这可能与你正在做的事情有任何关系,也可能没有任何关系,具体取决于您使用的语言以及您要执行的操作。实际上,当您开始对数据进行操作时,该值的行为方式可能完全错误。

于 2009-02-25T04:29:49.353 回答
0

Null 之于对象就像 0 之于数字。

于 2009-02-25T04:53:15.927 回答