3 ms·
Lately, "nulls should not be in your language" sounds like "reality should be simpler". Queue angry fist-shaking at the complexities of real life and a wistful
by feoren 2y ago
Lately, "nulls should not be in your language" sounds like "reality should be simpler". Queue angry fist-shaking at the complexities of real life and a wistful longing for a logical, functional, mathematical world. In reality, sometimes out of 8 questions, you only know the answer to 7, and the missing answer could be any of the 8. So you can:
1. Prevent the user from doing anything until they go find that 8th answer, which cascades into your tool not helping anyone until they have a complete, validated, full, historical data set.
2. Subdivide those 8 questions into having independent existence, which cascades into your entire application being a giant key/value store, and all the difficulties of null are simply replaced with the difficulties of "this key/value pair does not exist yet".
3. Add a sentinel value for "I don't know this yet", while feeling good about yourself because that sentinel value is not technically null, while introducing all the same exact issues that null causes, plus a bunch of infrastructure you have to write yourself. Basically: reimplement null, but worse.
4. Make the answers nullable.
It'd be like claiming that an IDE should simply not allow syntax errors to be entered at all. Errors would be completely impossible! Except at some point your user needs to actually write the thing, and you've just abandoned the idea of helping them. So they write it in some other editor, and then paste it into your IDE. Or you embrace the fact that incomplete work is OK.
Yes, nulls are generally way over-used. But disallowing them entirely is a fool's errand. Source: I've been that fool.
In before: "You should just be using Maybe<> everywhere" -- "Maybe" is just another word for "nullable". The only difference is the level of support you get from the compiler / type-checker. So that argument is "the compiler should help you get null right", which I completely agree with! There's still work to be done getting type checkers better at null. But that's a far cry from "null should not be in your language" and equating the use of nulls to a doctor not washing his hands.
- int_19h 2y agoOf course nobody is seriously arguing about ditching the notion of a sentinel value that indicates absence of value. The question, rather, is how to best handle that case. > In before: "You should just be using Maybe<> everywhere" -- "Maybe" is just another word for "nullable". The only difference is the level of support you get from the compiler / type-checker. The difference is that in languages with null references/pointers, all references/pointers are implicitly nullable - that is, it is indeed like "using Maybe<> everywhere". But when you have proper option types, you use Maybe<> not everywhere, but only where you know nulls can actually occur.
- feoren 2y ago> The difference is ... [whether] all references/pointers are implicitly nullable [or not] Right so we're narrowing down the actual problem here, which revolves around implicit vs. explicit, compiler support vs. on-your-own, static vs. dynamic typing. I agree with you that I strongly prefer explicit over implicit nullability, assuming it fits within the idioms of the language. But this is a stronger condition than static typing, so to believe "implicit nulls" is as irresponsible as a doctor not washing their hands, you* have to believe that everyone using dynamic typing is even more irresponsible than that. There's a place for dynamically typed languages, there's a place for implicit nulls, there's a place for strict type- and null-checking, there's a place for mathematically proving code correct. Engineering is always about tradeoffs -- it's way too simplistic to just say "null bad". * I know it wasn't literally you arguing that; I'm still partly addressing mrkeen here.
- int_19h 2y agoIt really depends on your definition of static typing. The thing that always made implicit nullability such a mess is precisely that it breaks the subtype relationship - that is, given some (reference) type Foo, you can have a null value of that, but none of the operations that normally apply to Foo apply to that null Foo value! In other words, it's not actually of type Foo, at least not under any sane definition of what a "type" is. So it was always a hole in static typing, and experience with all the grief caused by this hole is why we're moving away from it now, and why even the person who originally introduced this approach in the first place believes it was a mistake. But, yes, using dynamic typing would be even more irresponsible than using implicit nulls. And this is also something we've learned from experience - note how historically dynamically typed languages are acquiring some form of bolt-on statically checked typing (TypeScript, Python type annotations etc), and how quickly this is becoming the preferred way to write new code.
- mrkeen 2y agoCompletely agree with most of that, especially the problem that allowing nulls means that all of your values are now suspect. Just picking up on the term "sentinel" here, which I believe refers to using an actual member of the set to represent something else - which I am totally against, e.g. int itemCount = countItemsIn() - countItemsOut(); The rationale goes: no-one would ever think -1 is a valid count of items, so we can use it as an error sentinel. Then when countItemsIn() returns 1 and countItemsOut() fails, we deduce that itemCount is 2. So I guess a sentinal is a kind of dual to a null (which is a non-member of a set pretending to be a member)