4 ms·
I've alway felt "billion dollar mistake" is unreasonably hyperbolic. In retrospect it would be very nice to have a language level ability to define something a
by yarg 4y ago
I've alway felt "billion dollar mistake" is unreasonably hyperbolic.
In retrospect it would be very nice to have a language level ability to define something as optional, and require existence checking for such values;
But it wasn't so obvious at the time.
- highwind 4y agoI believe ML family of languages do not have null. And it was developed well before Java. So I don't think timing was the issue.
- pjmlp 4y agoEiffel also didn't, and additionally it supports value types (expanded classes).
- pkulak 4y agoCan you expand on that a bit? Why would having it built in to the language (Kotlin-style, for example) be better than not having the concept of null in the language at all and using something like the Rust Option? Personally, I like the Rust approach. It seems a bit more flexible and can evolve easier over time.
- cogman10 4y agoI think the main issue with the Rust option is they ended up with language syntactic sugar anyways. So in some ways, it's worse than the kotlin-style because reading `let baz = foo?.bar` could have all sorts of weird type implications. What is baz? Is it Option<Bar>? Is it Result<Bar>? Is it some user defined enum<Bar>? Who knows! You have to find that by looking up the foo definition. For kotlin, the answer is simple. "baz is a nullable Bar" Rust did this because interacting with the enum directly was cumbersome. The concept of "no value" is so integral to day to day programming that elevating it into the type system with "nullable types" makes more sense to me vs using generics trickery. Even if it's slightly less "pure".
- swsieber 4y agoI rarely if ever use the question mark with 'Option' when working with rust, and it's been just fine. I do use it a lot with Result though. But if you have any question about the return type you could[1] just look at the return type for the current function you're in. [1] I think there's a way to define custom types on nightly, and I wouldn't be surprised if it let you map custom types on Result/Option, but I don't actually know.
- ramranch 4y agoI am a fan of Rust, but I feel obligated to point out the return type of the current function does not provide any guarantees about the type which '?' was used on, even on stable. Since the generic requirement to use '?' with a return type of `Result<T,E>` is just `Into<E>`, you would still need to look at the called function since `From<E> for T` could be satisfied for (almost) any T.
- swsieber 4y agoAh, I did not realize that. Thanks for pointing that out!
- lmm 4y ago> So in some ways, it's worse than the kotlin-style because reading `let baz = foo?.bar` could have all sorts of weird type implications. Syntax sugar that works with a normal library type is much nicer than dedicated syntax for a special-case builtin, IME. > What is baz? Is it Option<Bar>? Is it Result<Bar>? Is it some user defined enum<Bar>? Who knows! You have to find that by looking up the foo definition. Sure, or if you have a decent IDE you just mouseover it. But that's no different than any other method. `let baz = foo.add(bar)` doesn't tell you what type foo or bar is, and I don't think I've ever seen anyone argue that it should (e.g. by requiring method names to be globally unique). > The concept of "no value" is so integral to day to day programming that elevating it into the type system with "nullable types" makes more sense to me vs using generics trickery. Even if it's slightly less "pure". I've found this isn't really true. Once you don't have language-level support nudging you to use it all the time, wanting to have a possibly-absent value is actually pretty rare. (E.g. a lot of the time you want to include a "reason" for why it's absent, so you want an Either/Result-like type - but if you're using Kotlin you end up using a nullable type because you're lazy and the language makes that easier. That's bad for long-term maintainability IME, especially because if you want to switch the nullable type for a result you have to change all your code - unlike Rust where you can switch fairly easily because ?. works the same way for both types).
- yarg 4y agoI really don't think that you want null in a modern language, at the runtime level there will eventually be a zeroed out reference field - but a programmer shouldn't have to deal with that. So I think we largely agree. (I left example syntax in another comment.)
- kaliszad 4y agoHaving nil (NULL, undefined whatever) that is falsey as is customary in Lisp/ Clojure is quite useful - the language family depends on it. Most things deal with it very well and it is a useful distinction that something wasn't setup or didn't return any meaningful value for whatever reason. Clojure has a powerful meta-data system which could be used for checking for errors if Clojure didn't depend on the host platform for that. Also, it seems, Common Lisp also has some ideas about how to handle things more or less in-line with other code without having a special system that you can't see most of the time. Btw. some of this can go very deep in the technology stack, there were computers (https://en.wikipedia.org/wiki/Ternary_computer https://en.wikipedia.org/wiki/Ternary_computer) with ternary logic (https://en.wikipedia.org/wiki/Three-valued_logic https://en.wikipedia.org/wiki/Three-valued_logic). Such a computer could improve many aspects of computing, e.g. density and therefore efficiency. It could improve reliability (things could be more explicit). You could divide by 3 precisely and efficiently. It could improve our understanding of logic by making this for most of us alien concept to a more practical and widespread tool.
- kaba0 4y agoIs there a difference between excluding null from a type and having basically syntactic sugar for ‘T | Null’ in the form of ‘T?’ ?
- MrJohz 4y agoYes. Having an explicit `Option<T>` type allows you to write things like `Option<Option<int>>`. This type is not possible in, say, Kotlin or Typescript. (Well, technically it is, but not using the native null types provided by the language.) Most of the time this isn't a very useful type to have, but it has the big advantage of being mechanically obvious. For example, consider a hashmap of optional types, something like `Map<String, Option<Player>>`. If we write a get method for the map, it should return an Option to indicate whether the value was present or not. But what happens if the value is present, but it is explicitly Null? The advantage of having an explicit option type is basically that it's easier to compose generics without having to understand what the values might be, which is usually what you want when using generics. That said, most of the time, if you've got an option of options of something, you're just going to flatten that type down anyway. E: Thinking about it, the other advantage is that it's easier to create a separate namespace for "methods that should exist when T might be null but are meaningless the rest of the time". For example, Rust's `Option` type has methods to map the internal value into another value, unwrap the value and panic if it was null, swap the value with a different one, etc. In languages which use null | T, the equivalent is usually to use Elvis operators and similar (obj?.field), but that requires more special casing.
- kaba0 4y agoI guess one could have ‘(T | None) | None’, which can automatically be flattened to ‘T | None’, couldn’t it? Also, the Map::get method that can return null is the problem here (even though your example is great and thanks for that, I didn’t think of it), something like ‘Option<Player?>’ should allow to differentiate between those cases. But I do remember reading that a bottom type does make typing rules harder (e.g. scala 3’s explicit nulls feature which is basically “T is non-nullable, write it as T | Null” is not sound)
- TJSomething 4y agoI mean, it doesn't take much for a class of errors that has existed for 55 years to cause a billion dollars in damage. Just in US labor costs, that's less than 5 hours per developer year. And I probably spend about 10 hours a year on average dealing with NPEs.
- yarg 4y agoSure, but most people see $1,000,000,000 and think only of the incomprehensible abstract - they see huge; they don't see what the value means. They certainly don't see the amortised cost across the decades long operation of a highly successful industry. So it comes across as a far deeper cut than the stubbed toe that it actually is.
- usrusr 4y agoConvenient hand-wave to distract from the question of wether or not it has been a mistake at all: "look, reverse-extrapolated to the individual man-year it's such a small number! Who cares about the sign!" That hour you spent chasing an NPE? Yeah, it happened. (really not that often though) The time saved by having a single, universally accepted way of implementing optionality, even it's inconveniently always on? That's invisible because we did not live through the alternative.
- topspin 4y ago> I've alway felt "billion dollar mistake" is unreasonably hyperbolic. I guess you can take that up with Turning Award winner C. A. R. Hoare FRS FREng. I'm going to go ahead and keep citing it despite being a epic underestimate. > But it wasn't so obvious at the time. It was, just not to the hackers that invented the languages plagued by these mistakes.
- insanitybit 4y ago> But it wasn't so obvious at the time. Sure it was. That was how it worked before. Hoare created null because of pressure from other programmers to write shitty code that looked like it worked even when it didn't.