今天我(再一次)取消引用一个未定义的对象的属性,导致我们的服务器崩溃。修复问题包括编写我最不喜欢的样板代码之一,转向
if (foo.bar.baz.id) {...
到
if (foo && foo.bar && foo.bar.baz && foo.bar.baz.id) {...
因为baz
恰好是边缘案例中的null
,这引发了异常。
所以我的问题是:为什么解除引用null / undefined这样的问题?当foo.bar.baz.something
/ undefined
/ foo
未定义时,为什么bar
只能安全地返回baz
?我理解技术为什么,并且对语言设计感兴趣为什么?是否有安全解除引用的语言?是否有一个真正的CS原因,为什么没有,或者它只是传统(继承形式C和它的老朋友)?
我可以想到一个原因,那就是在一个经常执行解除引用的动作中有更多"类型检查"-style逻辑,可以使语言减慢很多。但是我还认为大多数问题都可以通过在赋值/ if / etc.运算符中进行适当的异常处理来解决,因此解除引用可以继续按继续和"更高级"操作员会照顾偶尔的" oopises"。
我能想到的另一个原因是,自以为是的语言设计决定实施某种规则集来解决这个问题。
我非常感谢任何CS人员对这个问题有所了解。
答案 0 :(得分:1)
您所描述的不是引用的健全性/安全性,而是我称之为null monadic behavior,这意味着如果操作 T 采用空值 n 作为输入,其结果是 n ,无需任何计算。
您可能更熟悉C#或null propagation中的Objective-C equivalent一词。
即使SQL NULL在操作方面也是单一的。
出于性能原因,像C这样的语言没有空传播,在任何差异之前添加分支是一个巨大的减速。
其他语言的定义不同,因为它们具有不同的用途(例如,SQL用于处理数据并且需要标记为NULL)或不同的历史记录(Objective-C来自SmallTalk,其中对象不是被引用但是发出信号,因此向非对象发送消息是有效的,什么都不做)。
空值传播的负担并没有因为零传播而减轻,更糟糕的是 有时候很方便,但大多数都违反了“快速失败”的规则。
是否承认null was a big mistake in CS 解决方案是在Haskell中使用所谓的monads或在Java中使用Optional或在C#中使用nullable types或在C ++中使用boost::optional。 Null不是值,不是类型,而上面的所有解决方案都是值并且有类型 结合模式匹配或其近似,它们使用起来非常流畅,非常适合自我记录代码 它们也是安全的(对于语言允许的Type系统)