4 ms·
What's wrong with nil? Just legitimately curious, and I've never used Go at all.
by justinlardinois 11y ago
What's wrong with nil? Just legitimately curious, and I've never used Go at all.
- reycharles 11y ago> 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. https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retra...
- nostrademons 11y agoIt means that every single type in the language has one extra value it may contain, 'nil', and your code will crash or behave erratically if it contains this value and you haven't written code to handle it. This has caused billions of dollars in software errors (null dereferences in C/C++, NullPointerExceptions in Java, etc.). See "Null References: The Billion Dollar Mistake" by Tony Hoare, the guy who invented it: http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare http://www.infoq.com/presentations/Null-References-The-Billi... A better solution is an explicit optional type, like Maybe in Haskell, Option in Rust, or Optional in Swift. Modern Java code also tends to use the NullObject pattern a lot, combined with @NonNull attributes.
- sythe2o0 11y agonil in Go doesn't work that way. Most types cannot be nil.
- nostrademons 11y agoInteresting, because (reading up on this) value types can not be nil. How often does typical Go code use values vs. interfaces or pointers? It seems like the situation is pretty similar to modern C++, which also does not allow null for value or reference types (only pointers) and encourages value-based programming. Nil is still a problem there, but less of one than in, say, Java, where everything is a reference.
- sythe2o0 11y agoIn my own experience, nil basically only shows up when I've failed to initialize something (like forgetting to loop over and make each channel in an array of channels), or when returning a nil error to indicate a function succeeded. I've never run into other interfaces being nil, but I also haven't worked with reflection and have relatively little Go experience (~6 months). The code that I've written regularly uses interfaces and pointers, but I'd guess 80% works directly with values.
- lobster_johnson 11y agoBut a bunch types you do expect to work can: Slices, maps and channels. var m map[string]bool m["foo"] = 1 // Nil, panic var a []string a[0] = "x" // Nil, panic var c chan int <-c // Blocks forever This violates the principle of least surprise. Go has a nicely defined concept of "zero value" (for example, ints are 0 and strings are empty) until you get to these. The most surprising nil wart, however, is this ugly monster: package main import "log" type Foo interface { Bar() } type Baz struct{} func (b Baz) Bar() {} func main() { var a *Baz = nil var b Foo = a fmt.Print(b == nil) // Prints false! } This happens is because interfaces are indirections. They are implemented as a pointer to a struct containing a type and a pointer to the real value. The interface value can be nil, but so can the internal pointer. They are different things. I think supporting nils today is unforgivable, but the last one is just mind-boggling. There's no excuse.
- sythe2o0 11y agoI don't think using nil to represent uninitialized data is a major issue-- if it were possible to catch uninitialized but queried variables at compile-time, that could be an improvement, but we want to give the programmer control to declare and initialize variables separately. I agree the second case is a little silly.
- aninhumer 11y agoIt's perfectly possible to separate declaration and initialisation without using a null value.
- JanKanis 11y agoI don't think you're right that interfaces are implemented as a pointer to a struct. The struct is inline like any other struct, and it contains a pointer to a type and a pointer to the value, like `([*Baz], nil)` in your example. The problem is that a nil interface in Go is compiled to `(nil, nil)` which is different. That still makes this inexcusable of course.
- 11y ago
- sacado2 11y agoBeside the fact you're wrong (structs, arrays, bools, numeric values, strings and functions can't be nil, for instance), I'm always a little puzzled when I read the argument that "nil costs billions of $". First, most of the expensive bugs in C/C++ programs are caused by undefined behaviors, making your program run innocently (or not, it's just a question of luck) when you dereference NULL or try to access a freed object or the nth+1 element of an array. "Crashing" and "running erratically" are far from being the same. If those bugs were caught up-front (just like Java or Go do), the cost would be much less. The Morris worm wouldn't have existed with bound-checking, for instance. Second point, since we're about bound checking. Why is nil such an abomination but trying to access the first element of an empty list is not? Why does Haskell let me write `head []` (and fail at runtime) ? How is that different from a nil dereference exception ? People never complain about this, although in practice I'm pretty sure off-by-one errors are much more frequent than nil derefs (well, at least, in my code, they are).
- tome 11y ago> I'm always a little puzzled when I read the argument that "nil costs billions of $". $1bn over the history of computing is about $2k per hour. I would not be astonished if a class of bugs cost that much across the industry. > most of the expensive bugs in C/C++ programs are caused by undefined behaviors Sure, there are worse bugs. Why, then, waste our time tracking down trivial ones? > Why does Haskell let me write `head []` (and fail at runtime) ? Because the Prelude is poorly designed. > How is that different from a nil dereference exception ? It's not different, really. It's a very bad idea. > People never complain about this Yes we do. We complain about it all the time. It is, however, mitigateable by a library[1] (at least partially), whereas nil is not. [1] http://haddock.stackage.org/lts-5.4/safe-0.3.9/Safe.html#v:headMay http://haddock.stackage.org/lts-5.4/safe-0.3.9/Safe.html#v:h...
- sacado2 11y ago> $1bn over the history of computing is about $2k per hour. I would not be astonished if a class of bugs cost that much across the industry. It's not about knowing whether it's $1bn, or 10bn, or just a few millions. The question is to know whether fighting so hard to make these bugs (the "caught at runtime" version, not the "undefined consequences" version) impossible is worth the cost or not. Can you guarantee that hiring a team of experienced Haskell developers (or pick any strongly-typed language of your choice) will cost me less than hiring a team of experienced Go developers (all costs included, i.e from development and maintenance cost to loss of business after a catastrophic bug)? Can you even give me an exemple of a business that lost tons of money because of some kind of NullPointerException ?