I don’t buy that null is a billion dollar mistake.
Just because some dude with a fancy name said so doesn’t mean it’s true.
In particular, how many dollars does it cost to have null safety, and how many more does it cost to use it? I feel like folks quoting Hoare never even bother asking this question.
>In particular, how many dollars does it cost to have null safety
Close to none.
>Just because some dude with a fancy name said so doesn’t mean it’s true.
The dude is responsible for tons of what we take for granted. He is also the one who invented the null pointer, so there's that.
I'd take his expert opinion and experience on the matter over anybody referring to him as "a dude with a fancy name".
He didn't sit to calculate the money in damages from it's use - it was more of a figure of speech and a guess ("probably").
But it's very likely that the prodictivity losses from null errors, the consequences for security, program downtime, and so on, could trivially have costed 1 billion dollar in damages over the years - actually much more.
A single null error in one of the space code responsible from bringing down a NASA mission, could easily cost close to a billion itself.
In fact the memory safety industry (e.g. static analyzers and in memory analyzing tools), safety checks for nulls by consultants, and so on, industry easily has have 1 billion in profits over the last 40 years, so there's that...
FP is like a power saw tool you can use to cut wood or cut your fingers.
null pointer is more like a safety belt silencer. Convenient, especially when you don't want to be bothered or listen to the warning "beep". But there's no actual healthy benefit from using it.
That said, languages not providing a first class fast fixed precision/rational/decimal implementation for where precision is required, and this having been relegated to niche libs, is indeed a mistake.
You could reasonably argue that having languages make floating point arithmetic appear superficially similar to integer arithmetic despite key differences (inability to store exact results of various operations etc.) could qualify as such.
In many ways it is surprising that more languages haven't been given features to help avoid FP- arithmetic/rounding/conversion/formatting issues - e.g. just adding a built-in decimal type can help a lot (it's woefully common to have decimal amounts such as monetary quantities stored as FP, particularly in Javascript).
I've worked on international trading systems for a Fortune 500 broker-dealer that handled prices as IEEE-754 doubles, even though all currencies involved were decimalized. In the US, stock prices all have to be a multiple of $0.01, but in some markets, this minimum increment/decrement of a price ("tick size") is based on the price range, so lower priced stocks have finer-grained pricing. Determining if the customer was sending in a valid properly rounded price was a pain, as an upstream system had already parsed the price into a double and didn't pass our system the string representation.
so zero dollars were spent adding null safety to languages? False.
Zero dollars were spent converting code to those languages? False.
Zero dollars were spent solving problems uniquely created by not having the benefits of null? Also false.
> I'd take his expert opinion and experience on the matter over anybody referring to him as "a dude with a fancy name".
Sounds like a crappy way to reason about facts. Just because he did cool shit once upon a time doesn’t mean that we should believe him when he pulls numbers out of his ass.
I could also do an immitation of a dumb pedantic person and just answer your "First, zero dollars were spent adding null safety to languages? False", with: "I didn't say "zero" I said "Close to none". So PWNED! or something equally immature.
But one can also chose to be charitable. E.g. to understand that:
> Zero dollars were spent converting code to those languages? False.
Having null safety doesn't necessarily mean "converting". Could also mean not having nulls in a language to begin with. So "how many dollars does it cost to have null safety" in that case is zero. It's fixing the addition of nulls after the fact that can have a cost.
Even so, the conversion to null safety is not that costly (it's nothing like a rewrite, more like going around a program and fixing SQL Injection cases or XSS). It also makes evident many logical and safety errors in the initial program when done (many teams have written about such experience), and any cost has to be offset with the cost of those errors not happening anymore.
In any case, those are not costs of not having null to begin with in the language. They are costs of retrofixing code in ones that did have it.
> Zero dollars were spent solving problems uniquely created by not having the benefits of null? Also false.
There are no "problems uniquely created by not having the benefits of null". In fact, there are no "benefits of null" to begin with.
> Sounds like a crappy way to reason about facts.
Then again sounds like you didn't provide any facts. Just shown ignorance of computer science theory AND history, along with certainty and immature tone.
> Zero dollars were spent solving problems uniquely created by not having the benefits of null? Also false.
Do tell what those problems are? Whenever I hear people complain about this, they suffer from some fundamental misunderstanding of how null-safe languages work in practice
Not to defend the OP because I agree that null is bad. But, in my limited experiecne with Swift I just add a few characters at the right place and the null's are now incorrectly ignored and the warnings go away. So, the language gave me warning, but it didn't really force me to deal with it correctly. Is Swift a bad example of a null-safe language?
You literally tell it to break, a null-safe language will not actually prevent you from shooting your foot, it'll just make sure you do so knowingly. "foo!" is essentially a shortcut for
> I just add a few characters at the right place and the null's are now incorrectly ignored and the warnings go away. So, the language gave me warning, but it didn't really force me to deal with it correctly.
Personally I don't really see that as an issue, we're talking about null safe by default vs not safe by default. It being simple to break the null-safety is a good thing, as long as it requires you to do it deliberately
I don't think Hoare said that null was his mistake. I believe he said that the null reference was his mistake. In other words, null is not a reference, and treating null as a reference in all contexts conflates optionality and referentiality. The problem is that referentiality and optionality should be orthogonal concepts in type systems.
Nullability/optionality is clearly useful. References/pointers are clearly useful. The problem is making them a single concept in a type system, thus requiring all references/pointers to be nullable and making it very awkward (such as forcing the user to manually declare a wrapper type holding a boolean and the wrapped value) to have optional values without making them references.
The conflation of referentiality and optionality in the most popular static type systems also bleeds over into mental shortcuts used by many programmers in dynamically typed languages when thinking about the types of parameters expected by functions/methods. Had Algol W, C, etc. kept optionality and referentiality separate in their type systems, I think Python/Ruby/Lisp etc. programmers would likely think more carefully when passing None into fuctions/methods.
Lisp doesn't have None; it has nil. The Lisp nil was not inspired by the conflation of referentiality and optionality in popular static type systems. It predates them.
The conjecture about dynamically typed languages was regarding the cultural effect of the popularity non-null-safe static type systems on the current practice of coding in dynamic programming languages. It wasn't a conjecture about the structure of the type systems in those dynamically typed languages.
I find that even less plausible. I don't suspect that the null reference practices in, say Java, have any influence on the way people work in Lisp.
Optional parameters in Lisp take on a value of nil when the argument is omitted (unless a different default is specified). Various search functions return nil when an item is not found: find, member, gethash, ...
The practices around these conventions may, at times, resemble work in static languages with null references, but there is no cause-and-effect there.
After using languages with the Option type [0] and exhaustive pattern matching [1] (OCaml was my first exposure), I can definitely say that bare null pointers, without using the prior two things I mentioned, are a mistake.
It's not that null as a concept is a mistake, since a Option type has both Some and None, it's that in most mainstream languages, people have to implicitly deal with them rather than having the compiler checking it for them explicitly. And if the computer can check our work for us, why do we have to do it ourselves?
That's why it leads to mistakes and is why it's called a billion dollar mistake, because I'm sure at least 1 billion (possibly even 1 trillion) dollars worth of manpower, lost revenue and time have been spent dealing with nulls.
The null-safe language I've used the most is Rust. It is far from the hardest part of the language to learn, but the biggest impact day-to-day is that when constructing a new object, initial values for all the non-nullable fields have to be provided all-at-once. You can't have a constructor which is passed a `this` object full of nulls then fill in all the values later, instead the object is constructed whole in a single operation.
Otherwise the explicit distinction between T and Maybe T just makes life easier.
Ironically you can do that in dart. If you declare your member late and never initialise it you can run into what amount to a null reference exception.
null itself isn’t the billion dollar mistake, it’s that literally every object can be T|null. Undefined or null is actually quite useful, as long as you’re expecting it.
That's the exact same issue you run into if you suddenly find out that you need to use a different type (because.. Yeah, that's exactly what it means). That doesn't mean you should default to writing all your code without types
Null safety means you can't dereference a null pointer. Therefore something like `T | null` is not possible, because `null` doesn't exist. What you'd have instead is an Option or Maybe type and a compiler that requires exhaustive handling of it.
That's about as unclear as your previous comment, at best. At worst, it's just wrong.
The typechecker would only allow on the union operations which are allowed on both types, anything beyond that would first have to use type checks or assertions in order to split the union.
That's actually a good point. I made assumptions about the meaning of the term `null` that can't be true in every language that uses it.
There are some exceptions to the behaviour you describe, like for instance with C# which for the longest time only allowed value types to be annotated as nullable, and only very recently extended this to reference types, and only as an opt-in feature, and the type checker only throws warnings, etc. That would be a case of a language which is not "null safe" but provides faculties for accomplishing that.
Here's my take: yes, it was a billion dollar mistake. But it wasn't null on its own that was the mistake, it was making it impossible to opt out of. It's incalculable how much that breaks pretty much all of the mental modelling you do about systems. Null safety is the only way to get back to where we want to be.
I believe that's why Hoare said that the null reference, not null, was his billion dollar mistake.
It was a simple one-liner in his type checker "if getting a type error in a cast, if the source type is nulltype and the target type is any kind of reference, always let the cast happen". His (biased by hindsight) recollection is that he even felt it was a bit dirty at the time, but made a lot of code shorter.
But anecdotally I've experienced many null-pointer runtime errors, to the point where I very strongly believe having strict or even half-decent null checking reduces the amount of runtime errors my code produces non-negligibly. I'm sure not everyone has the same experience, but I strongly prefer languages with null safety (AKA marking a type "non-null", and then the compiler makes a good effort to ensure it's not null).
At 100k per year, 1B is a mere 10,000 man-years or with 4.4M devs in the US, that's less than one work day per currently-working dev across their entire career.
I'm sure that I've spent weeks worth of time doing this in one form or another and I doubt that I'm alone here. By that metric, it's more like the 100B mistake.
There is a cost to pay up-front in having null safety in your codebase, but the savings come back over time. Can we easily measure whether or not it truly makes a difference? Maybe not, but I'm jaded enough to gladly pay that cost, knowing I can think about whether or not something is null once, versus having to forever wonder if the complicated code I need to interact with is going to bite me later.
I agree. I write Java for many years and never had I any particular issue with nulls. I used Kotlin for a while and null-safety is a compromise which often leads to verbose and unnecessary code.
I don't have any particular side on this battle, I can use both approaches. I just don't think that it's a big deal.
I've come to the conclusion that in the generic business software world, removing null would be a trillion dollar mistake.
Instead of applications simply crashing, millions of hours would be wasted trying to clean up bogus values (e.g. "changeme", empty string, etc) from databases and calculate what should have been there instead.
Nobody is removing null because null itself is not the mistake.
The mistake was to say like in Java that null is a valid value for every possible class under the sun.
What Kotlin & cie are saying is that if your parameter is a RandomGenerator, null should not be allowed there because null is not a RandomGenerator. Null is null and that's it.
If you search something in a map, your value will be of type "String?", that is "String | null" and that's completely fine, the compiler will assist you with that too.
I'd even question whether a function whose job it is to "find" something should be able to return a Nullable value - or at least, it shouldn't be possible to just force such a value to be treated as an actual value (as is allowed by Swift/Kotlin/C# etc with "force unwrap" type operators). I'd rather see a language with built in support for functions that can either return one type or another, whereby the caller must provide two code paths to deal with each. And yes this would imply you could never simply call such a function and store its return value in a variable.
That was the initial intent behind Kotlin's Result<T> type; it was only allowed to be a return value from compiler-blessed functions (runCatching and friends) and was not allowed to be stored in a property.
The problem became this additional function color; also, no language construct can prevent users from implementing "fun Result<T>.unwrap(): T" and crashing the program in the failure case.
OK, you could also restrict functions that accept Result<T> (as a receiver or parameter), but now you force resolution of that type by the direct caller of the function returning Result<T>. So really this becomes a worse version of checked exceptions, which (thankfully) don't exist in Kotlin.
I think you misunderstand what null safety really means. It’s not that values can’t be null/None/Empty. It’s just that it’s made explicit where this is the case and where it’s not the case.
Without null safety everything is implicitly maybe null.
In other words, the problem is that null is treated as if its class is a subclass of all classes. All of a sudden, you've introduced a superset of all of the problems with multiple inheritance. (Nulltype inherits from everything!) You've also made it nearly impossible to adhere to the Liskov substitution principle (unless none of your classes can do anything, making all object instances equivalent at runtime).
In type theory, a type that is a supertype of all types is called a "top type" and a type that is a subtype of all types is a "bottom type". It's okay to have one or more bottom types in a type system, as long as it's not possible to create instances of them. Unfortunately, in most of the popular statically typed languages, null is an instance of a bottom type. One reasonable use of a bottom type would be to allow a variable to assume one of many types, and require a dynamically-checked upcast when actually passing that variable to a function that cared about its types.
The more I think about it, the more it sounds like hubris. If Tony Hoare hadn't made that design choice then somebody else somewhere would have.
I think the sentiment behind the quote and its widespread use is correct however. It definitely does seem like the tradeoffs of null safety are worth it in the vast majority of cases. Most of us who use GC languages with massive runtimes sacrifice a lot more performance for a lot less value.
I am supposing that the further back in time you go, the more likely it is that simplicity and performance would influence language and compiler design. As opposed to developer experience.
I think this can be supported by the fact that these fancy language features have been successfully implemented for decades and are only now showing up in mainstream languages.
He invented it for algol, a language with sufficient complexity that it was basically impossible to implement. The process of creating c involved stripping off everything but the most necessary features. Null references made it, nullable types likely wouldn't have.
> The process of creating c involved stripping off everything but the most necessary features. Null references made it, nullable types likely wouldn't have.
Nonsense.
The entire point of Hoare's design was to have type safe references, which C pointers most definitely are not. The entire point of references was that they not be pointers.
Just because some dude with a fancy name said so doesn’t mean it’s true.
In particular, how many dollars does it cost to have null safety, and how many more does it cost to use it? I feel like folks quoting Hoare never even bother asking this question.