4 ms·
I would recommend looking Haskell's Maybe and Rust's Option type to get a better idea of how this can be solved -- and how this article isn't really overrated (
by cyphar 8y ago
I would recommend looking Haskell's Maybe and Rust's Option type to get a better idea of how this can be solved -- and how this article isn't really overrated (just commonly misunderstood).
They allow for explicit NULL-ness (which is a necessary concept) without falling into the trap of making everything implicitly possibly NULL. And when NULL-ness is explicit you are then forced to explicitly handle it in order for your program to compile (in the case of Haskell and Rust).
- jchw 8y agoI dunno why everyone's assuming I don't know about the common solutions to the problem. TypeScript does explicit nullness without needing monads, and I actually mentioned that one. I still disagree that this is not overrated. Go's idea of nil, for example, seems OK to me, and the language would need to be way more complex to fix it. For example, it would need a type system with explicit nullness, or maybe even actual generics. But it mostly doesn't matter because doing things with nil doesn't crash nearly as much in Go. Like a nil slice just acts like an empty slice. You can even append to nil and it returns a non nil slice. You can call methods on nil. Etc. The trouble with getting rid of nil to me is that it requires you to either have values at all times, or deal with the possibility that you don't at all times. Go has the very very nice property that you can initialize any type to a zero value and it should work as an "empty" object. Pointers without nullability don't have a zero value. Fixing nulls at the cost of getting rid of Go's properties for zero values would not be worth it.
- cyphar 8y agoDon't get me wrong, Go does nil a lot better than some other languages (being able to call methods on a nil is sometimes a good thing depending on how your methods handle it -- most methods don't handle it well at all). The fact that even most map operations (access and deletion) also "just work" is really useful. But I think you're over-selling the zero values feature of Go. It is very rare to see third-party libraries that have zero values which do anything but cause a NPE when you try to use them -- mainly because they embed pointers and then you have the same implicit NULL-ness that causes NPEs everywhere. It is great that the core language managed to get zero values right in most cases, but it's far from being as wide-spread as you might hope. Also (nil interface != nil pointer that fulfills interface) is a very common mistake I see in Go code, and while it's not necessarily related to the existence of a nil value it is still related to the general concept of nil in Go. [And on the TypeScript comment -- you don't really need monads for Option<> or Maybe types. You just need algebraic types -- and TypeScript has those. Haskell does use Monads for Maybe, but that's because Haskell has many other type-theory things that make it necessary to support using Maybe as a monad.]
- jchw 8y agoI'm not overselling zero values in Go. Simply try to envision the cascading consequences on the language if you removed the zero values; no existing Go code would work, and I think the language would need to shift so much that even hello world couldn't be automatically translated to such a language. All to prevent a single type of runtime error among many, one that Go developers are not complaining about the way that Java, JavaScript, C developers have.
- cyphar 8y agoNobody is arguing that Go should have algebraic types and ditch zero values, so I don't know why you're harping on this point. Now -- it would be somewhat nice because errors would be much more reasonable to handle (the new "check" proposal is okay but still quite flawed) but you're right that it would either be far too complicated or old code wouldn't work anymore. Go has already made it's bed when it comes to nil values, but that doesn't mean that all new languages should follow suit -- because Go's nil handling isn't all sunshine and roses (nil interfaces -- for obvious reasons -- cause NPEs). As I've said, Go does nil basically as well as you can without having algebraic types. But given the semi-anecdotal evidence that I've definitely seen my fair share of NPEs in production Go code in the past 5 years, it's clear that it's not sufficient.
- jchw 8y agoI've had I believe a single NPE in Go in production for three years. It's a blip on the radar. If you are testing your code, how do you even get an NPE in production? Seriously, it should be rare. I've actually had more trouble with channels than pointers in Go. Also, if the situation is really so bad.... How come no one cares? I saw no Go 2 proposal to fix this situation, just generics, less boilerplate for error handling. But nothing about nil pointers. It's not at the top of anyone's list. Generally, people feel comfortable with nil in Go in ways they don't in JS and C. I will take that for fact even though certainly you disagree. So why do people feel more comfortable with nil in Go? Because nil is not an error. In C and JS, there are cases where nil is treated as an error. For example, getElementById returns null when it finds nothing. If you designed this API in Go, you'd instead return nil, PLUS an error (or Perhaps, a bool, if no other possible errors exist.) You can argue semantics but in Go it's generally held that if you aren't returning what the user wanted you should return an error. Exceptions to that exist but probably mostly just string manipulation functions. This convention is so strong, though, that it nearly eliminates nil pointer errors caused by edge cases. Most nil pointer errors you DO hit in Go are: 1. Set on nil map 2. Send on nil channel 3. Failing to check error The thing is, if you are writing production code, and writing tests for your production code, this shouldn't even make it to your code repository. It's virtually a non issue. Even better; in C and C++, when you hit a null pointer, you don't get an NPE. You get a segfault. Everything dies. Kaput. Go is obviously not alone in not having this issue, but it's surely worth noting that it doesn't. Go is not going to die some day because it's "not safe enough" - it's absolutely safe enough to write reliable code. The difference is, writing reliable code in Go is easy because for the most part, all you have to do is follow conventions and unit test. Same cannot be said about C and JS where you will inevitably get blindsided by sharp edges. If you are in a situation where you can't have runtime errors, it's a poor fit. I don't know personally any developers that are in this situation. If your code doesn't put lives on the line, its OK to have a runtime error. Most of your real outages will be due to other bugs, and probably more of then will be due to other people's bugs, natural disasters, human operator error, bad configuration pushes, etc. If you really think Go NPEs are even a significant portion of Go reliability issues I'm gonna need more evidence.
- taco_emoji 8y agoBut nobody's talking about "getting rid of nil" wholesale. They're talking about getting rid of null/nil reference exceptions via the mechanisms of non-nullability (having the option to define some variables as non-nullable) and null guards (a compiler which forces a null check before any operation which requires a non-null value). This way you still have null, but with compile-time guarantees that you'll never get null when you didn't expect it.
- jchw 8y agoThat's the same as the TypeScript approach, but its never going to happen in Go because of the relatively small gain for a massive jump in complexity in the compiler.