Null References, the Billion Dollar Mistake
jonr writes "'I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years. In recent years, a number of program analysers like PREfix and PREfast in Microsoft have been used to check references, and give warnings if there is a risk they may be non-null. More recent programming languages like Spec# have introduced declarations for non-null references. This is the solution, which I rejected in 1965.' This is an abstract from Tony Hoare Presentation on QCon. I'm raised on C-style programming languages, and have always used null pointers/references, but I am having trouble of grokking null-reference free language. Is there a good reading out there that explains this?"
It's hard to imagine life without the null pointer! That being said, the author is not really responsible for billions of dollars of mistakes, the programmers are.
If there is one thing I'll complain about, it's the choice of the value 0. It's almost impossible to trace it. When we do hardware debug of chips, we prefer to use a much more visible value such as 0xdeadbeef for instance. Otherwise a bad pointer will bland too much with all the uninitialized values out there.
In assembly, null has no particular meaning. If you dereference an address, you can do it in any range you like. It's just that 0 on most machines was not a good place to store anything, since it would typically be used to boot the OS or some other critical IO function that you don't want to mess up with. Thus null was born.
If you're familiar with SQL, then a simple "MyColumn NOT NULL" definition should explain it. Basically, the value can never be set to a null value. Attempting to do so is an error condition itself.
In fact, DB design is a pretty good analogy for the concept as databases often are forced to wrestle with this issue.
Consider for a moment how you would design a database that has absolutely NO null references. Not a one. Zip, zero, nada. Obviously the best way of accomplishing such a database is to denormalize any value that might be null. So if Address2 is optional, you would want to split Address into its own table with a parent key pointing back to the user entry. If the user has an Address2 value, there will be a row. If the user does NOT have an Address2, the row will be missing. In that way, empty result sets take the place of null values.
In terms of programming languages, there are a varity of ways to map such a concept. Collections are a 1:1 mapping to result sets that can work. If you don't have any values in your collection, then you know that you don't have a value. Very easy. Similarly, you can be sure that none of the values passed to a function or method will ever contain a null value. Cases where you might want to pass some of the values but not all can be handled either by method overloading (e.g. Java) or by allowing a variable number of parameters. (e.g. C)
Some pieces of programming would become slightly more difficult. For example, 'if(hashmap.get("myvalue") != null)' would not be a valid construct. You'd need to perform a check like this: 'if(hashmap.exists("myvalue")'
Of course, the latter is the "correct" check anyway, so the theory goes that the software will be more robust and reliable.
Javascript + Nintendo DSi = DSiCade
Umm... no? The first thing done is usually a superclass called "Object". If you don't extend anything else, you extend Object. Depending on the language, the superclass of Object would either be self-referential or the option to obtain a superclass wouldn't exist. (The latter being the "correct" solution. See my next statement for why.)
That's actually a problem. Null is a piece of data that represents the absence of data. The paradox here should be obvious. If the data doesn't exist, why do we create data about it not existing? If I have no apples, do I have an object that represents my lack of apples? No, I simply have no apples. At best, I might have a special container for apples. If it's empty, then I can infer that I have no apples. Just as a program can infer the absence of data through an empty collection.
There's a lot of things wrong with C as a language. Don't try to use those as arguments. (Remember, C is more or less high-level assembly. On the scale of comp-sci it barely even rates. Its popularity stems from the excruciating slowness of computers in days gone by.)
Javascript + Nintendo DSi = DSiCade
If you use a sane class for references that could possibly be null (like Option (aka Maybe in haskell) then your compiler will *force* you to handle the null case.
This is where null went wrong, at least in statically typed languages: it's a hole in the type system that errors fall through into your program. When coding in Java, I make an explicit point to never return null from a method; if I have a situation where no reasonable return value might exist, I use the Option class from functionaljava.org and thus force the client to handle the possibility of the method not returning sensible data. Since Option obeys the monad laws, it's easy to chain together multiple things that might fail (with the bind or flatMap operations.)
The problem with Pascal strings is that it's easy for a short-sighted implementer to paint themselves into a corner. It's all very well and good to say "The first two bytes in a string are used to indicate the length of the string" but then what do you do a decade from now when a 16bit string is laughably small? The benefit of NUL terminated strings is that there length is only limited by the memory available to you and yet are forward and backward compatible by decades.
Syllable : It's an Operating System
Stroustrup's "C++ Programming Language" book introduces a concept called "resource acquisition is initialisation" that was eye-opening enough to me that it forever changed the way I think about code, and also seems relevant to your point.
The basic idea is that an object is always meant to represent something tangible. As an example, consider the design of file object that abstracts file I/O operations. As a developer, I've come across this one several times, it is normal that such objects have open and close methods, however that makes the design of the object in contradiction with Stroustrup's concept because open/close provided as methods rather than only called in the constructor/destructor means the object may be in existence yet be in a state where it is not associated with an open file. You basically have to grok that having a file object around that doesn't directly map to an open file just adds overhead to the system and is basically bad OO design in that in some sense that object is meaningless.
Apply the same concept to a reference and you have your answer. If a reference is pointing at nothing, then what is its purpose? The only thing a NULL reference is good for is when the software design ascribes a special meaning to the value NULL. Instead of just meaning address location 0, it gets subverted to mean "variable unassigned" or the "tail node of list" or somesuch. Ascribing multiple meanings to a variable value (especially pointers/references that are only ever meant to hold memory addresses) is one example of bad programming practice known as programming by side-effect which most people agree should be avoided.
Another point is that in most OO lanugages, references have an extra benefit of being more strongly typed than pointers, menaing that reference is guaranteed to only ever be pointing at an instantiated object of its specific type. That guarantee also gets broken when a reference can be NULL.
The first OS I encountered was tape-based. And it prefilled user memory with a "core constant".
This was a subroutine jump to an abort routine which printed the return location - which in turn told you where you had improperly jumped to and dumped all your registers, followed by the memory itself if that was authorized. (That was all the info that was left by the time the OS got control.)
The walls of the computing center contained posters giving this value as it would appear if printed as various types of values (integer, floating point, complex, ...).
Another machine I dealt with back then was a typesetting device using a Data General Nova and the company's homebrew OS (designed by Mark Weiser of Xerox Parc fame, based on work by Djikstra and Riddle). It had a debugger entry that could be reached by a one-word instruction which we would insert as breakpoints and also use to fill unused memory. The hex form of this was "0c0f". When the machine hit a breakpoint we said it had "coughed".
Bantam Dominique roosters crow a four-note song. Once you've heard it as "Happy BIRTHday" you can't NOT hear it that way
Yeah, I know this is a joke, but I've still got to point out the following: a positional numbering system doesn't actually need a zero digit. Throw out zero, and use instead a digit 'X', whose value is ten. Then you get:
1, 2, ..., 9, X, 11, 12, ..., 19, 1X, 21, ..., 98, 99, 9X, X1, X2, ..., X9, XX, 111, ...
8X = 8 * X + X (ninety)
X2 = X * X + 2 (one hundred two)
Are you adequate?