没有 null 的语言的最佳解释 [英] Best explanation for languages without null
问题描述
当程序员抱怨 null 错误/异常时,经常有人问我们没有 null 怎么办.
Every so often when programmers are complaining about null errors/exceptions someone asks what we do without null.
我对选项类型的酷炫有一些基本的了解,但我没有知识或语言技能来最好地表达它.对于以下以普通程序员平易近人的方式编写的以下内容的优秀解释是什么,我们可以将其指向该人?
I have some basic idea of the coolness of option types, but I don't have the knowledge or languages skill to best express it. What is a great explanation of the following written in a way approachable to the average programmer that we could point that person towards?
- 不希望引用/指针默认为可空
- 选项类型的工作原理,包括简化检查空情况的策略,例如
- 模式匹配和
- 单一理解
推荐答案
我认为为什么 null 不受欢迎的简洁总结是无意义的状态不应该被表示.
I think the succinct summary of why null is undesirable is that meaningless states should not be representable.
假设我正在建模一扇门.它可以处于以下三种状态之一:打开、关闭但未锁定和关闭并锁定.现在我可以按照
Suppose I'm modeling a door. It can be in one of three states: open, shut but unlocked, and shut and locked. Now I could model it along the lines of
class Door private bool isShut private bool isLocked
很清楚如何将我的三个状态映射到这两个布尔变量中.但这留下了第四个不需要的状态:
isShut==false &&isLocked==true
.因为我选择作为我的表示的类型承认这种状态,所以我必须花费精神上的努力来确保类永远不会进入这种状态(也许通过显式编码一个不变量).相反,如果我使用具有代数数据类型或检查枚举的语言来定义and it is clear how to map my three states into these two boolean variables. But this leaves a fourth, undesired state available:
isShut==false && isLocked==true
. Because the types I have selected as my representation admit this state, I must expend mental effort to ensure that the class never gets into this state (perhaps by explicitly coding an invariant). In contrast, if I were using a language with algebraic data types or checked enumerations that lets me definetype DoorState = | Open | ShutAndUnlocked | ShutAndLocked
然后我可以定义
class Door private DoorState state
再也不用担心了.类型系统将确保
class Door
的实例只有三种可能的状态.这就是类型系统擅长的 - 在编译时明确排除整类错误.and there are no more worries. The type system will ensure that there are only three possible states for an instance of
class Door
to be in. This is what type systems are good at - explicitly ruling out a whole class of errors at compile-time.null
的问题在于,每个引用类型都会在其空间中获得这种通常不受欢迎的额外状态.string
变量可以是任何字符序列,也可以是这个疯狂的额外null
值,它没有映射到我的问题域中.Triangle
对象有三个Point
,它们本身有X
和Y
值,但不幸的是Point
s 或Triangle
本身可能是这个疯狂的空值,对我正在工作的图形域毫无意义.等等.The problem with
null
is that every reference type gets this extra state in its space that is typically undesired. Astring
variable could be any sequence of characters, or it could be this crazy extranull
value that doesn't map into my problem domain. ATriangle
object has threePoint
s, which themselves haveX
andY
values, but unfortunately thePoint
s or theTriangle
itself might be this crazy null value that is meaningless to the graphing domain I'm working in. Etc.当您确实打算对可能不存在的值进行建模时,您应该明确选择它.如果我打算模拟人的方式是每个
Person
都有一个FirstName
和一个LastName
,但只有一些人有MiddleName
s,那么我想说一些类似When you do intend to model a possibly-non-existent value, then you should opt into it explicitly. If the way I intend to model people is that every
Person
has aFirstName
and aLastName
, but only some people haveMiddleName
s, then I would like to say something likeclass Person private string FirstName private Option<string> MiddleName private string LastName
这里的
string
被假定为不可为空的类型.然后,在尝试计算某人姓名的长度时,无需建立棘手的不变量,也不会出现意外的NullReferenceException
.类型系统确保任何处理MiddleName
的代码都考虑到它是None
的可能性,而任何处理FirstName
的代码都可以安全地假设那里有一个值.where
string
here is assumed to be a non-nullable type. Then there are no tricky invariants to establish and no unexpectedNullReferenceException
s when trying to compute the length of someone's name. The type system ensures that any code dealing with theMiddleName
accounts for the possibility of it beingNone
, whereas any code dealing with theFirstName
can safely assume there is a value there.例如,使用上面的类型,我们可以编写这个愚蠢的函数:
So for example, using the type above, we could author this silly function:
let TotalNumCharsInPersonsName(p:Person) = let middleLen = match p.MiddleName with | None -> 0 | Some(s) -> s.Length p.FirstName.Length + middleLen + p.LastName.Length
没有后顾之忧.相反,在对字符串等类型具有可为空引用的语言中,则假设
with no worries. In contrast, in a language with nullable references for types like string, then assuming
class Person private string FirstName private string MiddleName private string LastName
你最终会创作像
let TotalNumCharsInPersonsName(p:Person) = p.FirstName.Length + p.MiddleName.Length + p.LastName.Length
如果传入的 Person 对象不具有所有非空的不变式,则会爆炸,或者
which blows up if the incoming Person object does not have the invariant of everything being non-null, or
let TotalNumCharsInPersonsName(p:Person) = (if p.FirstName=null then 0 else p.FirstName.Length) + (if p.MiddleName=null then 0 else p.MiddleName.Length) + (if p.LastName=null then 0 else p.LastName.Length)
也许
let TotalNumCharsInPersonsName(p:Person) = p.FirstName.Length + (if p.MiddleName=null then 0 else p.MiddleName.Length) + p.LastName.Length
假设
p
确保第一个/最后一个存在,但中间可以为空,或者您可能会进行抛出不同类型异常的检查,或者谁知道是什么.所有这些疯狂的实现选择和要考虑的事情都会突然出现,因为存在您不想要或不需要的愚蠢的可表示值.assuming that
p
ensures first/last are there but middle can be null, or maybe you do checks that throw different types of exceptions, or who knows what. All these crazy implementation choices and things to think about crop up because there's this stupid representable-value that you don't want or need.Null 通常会增加不必要的复杂性.复杂性是所有软件的敌人,您应该在合理的情况下努力降低复杂性.
Null typically adds needless complexity. Complexity is the enemy of all software, and you should strive to reduce complexity whenever reasonable.
(请注意,即使是这些简单的例子也有更多的复杂性.即使
FirstName
不能为null
,string
也可以表示""
(空字符串),这可能也不是我们打算建模的人名.因此,即使使用不可为空的字符串,我们仍然可能表示无意义的值".同样,您可以选择在运行时通过不变量和条件代码或通过使用类型系统(例如具有NonEmptyString
类型)来解决这个问题.后者可能是不明智的(好"类型通常在一组常见操作上是封闭的",例如NonEmptyString
不是在.SubString(0,0)
上封闭),但它演示了设计空间中的更多点.归根结底,在任何给定的类型系统中,有些复杂性将非常擅长摆脱,而其他复杂性本质上更难以摆脱.关键对于这个主题,几乎在每个类型系统中,从默认情况下可空引用"到默认情况下不可空引用"的变化几乎总是一个简单的变化,它使类型系统变得非常重要更好地应对复杂性并排除某些类型的错误和无意义的状态.因此,如此多的语言一次又一次地重复这个错误,这真是太疯狂了.)(Note well that there is more complexity to even these simple examples. Even if a
FirstName
cannot benull
, astring
can represent""
(the empty string), which is probably also not a person name that we intend to model. As such, even with non-nullable strings, it still might be the case that we are "representing meaningless values". Again, you could choose to battle this either via invariants and conditional code at runtime, or by using the type system (e.g. to have aNonEmptyString
type). The latter is perhaps ill-advised ("good" types are often "closed" over a set of common operations, and e.g.NonEmptyString
is not closed over.SubString(0,0)
), but it demonstrates more points in the design space. At the end of the day, in any given type system, there is some complexity it will be very good at getting rid of, and other complexity that is just intrinsically harder to get rid of. The key for this topic is that in nearly every type system, the change from "nullable references by default" to "non-nullable references by default" is nearly always a simple change that makes the type system a great deal better at battling complexity and ruling out certain types of errors and meaningless states. So it is pretty crazy that so many languages keep repeating this error again and again.)这篇关于没有 null 的语言的最佳解释的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!