70 ms·
NULL: The worst mistake of computer science? (2015)
- laichzeit0 8y agoSo how does NaN in Python and NA in R relate to NULL? I know Python has the None type, but it's not the same as NaN. One of the most annoying things in Numpy is that there is no way to indicate that an integer value is "missing", similar to NaN for floats. In R both integers and strings can be NA (if I remember correctly). So for numeric types at least, there is definitely the need to somehow indicate that a value is "missing".
- hurrrrr 8y agoNumpy has masked arrays [1]. Though I can't say how well they work. [1] https://docs.scipy.org/doc/numpy/reference/maskedarray.generic.html#rationale https://docs.scipy.org/doc/numpy/reference/maskedarray.gener...
- Nullabillity 8y agoNaN is not the same as None/null, and using it that way is asking for bugs. None/null is for missing/unknown values (which may be a reasonable thing in your domain), while NaN is for the result of an illegal/undefined operation (which is definitely a bug, such as division by 0).
- jillav 8y agoWhether null/NULL is a good a idea or not (I like it, just yesterday it saved my ass) it saddens me that more and more articles I stumble upon are made to criticize technologies but not to talk about solutions or innovations
- saagarjha 8y agoThe article talks about a solution, suggesting that optionals are a much better way to handle this.
- thwy12321 8y ago"The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt. – Rob Pike" When did computer science become about hand holding? has it always been this way? Look at react. It was designed to force functional programming concepts in an OOP manner. Is the future of programming the implementation of tightly controlled interfaces with extreme type safety? I would argue thats where we are going. Things are becoming less expressive, not more.
- skgoa 8y agoIt's the Java approach. Don't give people footguns and force them to write software in a readable, testable, maintainable style. It works extremely well in software engineering, because you want systems that work reliably and that can be maintained/extended by any other engineer at your org. In the professional software engineering world, "clever" programmers are almost always a horrible drag on their team.
- masklinn 8y agoIt's not the Java approach at all. The first languages to remove ubiquitous nulls (e.g. MLs) were looking to increase expressivity and modeling abilities, not to force bondage and discipline upon developers.
- coldtea 8y ago>When did computer science become about hand holding? When people with pragmatic goals want to get large teams of new programmers productive fast, and can't expect everyone to be able to fend on their own or can afford the cost of accumulated mistakes. >Look at react. It was designed to force functional programming concepts in an OOP manner. Whatever that means, as React has little to do with "OOP manner".
- 8y ago
- kekzzz 8y agoWhy can't people just get over it and stop blaming the language for their own sloppy code?
- turbinerneiter 8y agoBecause everybody is working on the limit of complexity they can comprehend, so there is no headspace left for dealing with bad ergonomics. We need all the help we can get.
- jstimpfle 8y ago> everybody is working on the limit of complexity they can comprehend That's the problem right there.
- timvisee 8y agoWell, apparently it doesn't work that way.
- goto11 8y agoBecause languages are tools, and tools should help you do your job, not hinder you.
- masklinn 8y agoWhy can't people just get over it and stop blaming users for the tools failing them?
- edoo 8y agoNulls in strongly typed languages can get rather weird but from a C/C++ perspective it is the same as 0. nullptr is just a correctly casted 0.
- saagarjha 8y agonullptr is actually "a prvalue of type std::nullptr_t" or something like that. Since C++11 NULL is the same as nullptr.
- edoo 8y agoAnd it is directly convertible to a bool. The primary effectiveness seems to be with operator overloading where a NULL could trigger an integer method instead of a pointer method.
- ape4 8y agoYes that's the example given here. https://en.cppreference.com/w/cpp/language/nullptr https://en.cppreference.com/w/cpp/language/nullptr So its an improvement but not exactly a game changer.
- saurik 8y agoTo the small extent to which "since C++11" is even relevant to "from a C/C++ perspective", even in C++11 NULL is still allowed to simply be defined as 0.
- jcelerier 8y agoActually nullptr is a struct.
- trhway 8y agoreminds that https://en.wikipedia.org/wiki/Blind_men_and_an_elephant https://en.wikipedia.org/wiki/Blind_men_and_an_elephant :)
- grive 8y ago
- jeromebaek 8y agoAt least C# has the syntactic sugar to easily check for null references which lets you avoid the horrors of code like `(if s != null && s.length)`. Instead you can type `s?.length`. Never have I appreciated syntactic sugar as much.
- rusk 8y agoA lot of higher languages use the concept of truthiness such that null, or 0-length both evaluate to false: if (s) then do stuff with s;
- ohazi 8y agoOther candidates: - null terminated strings - machine dependent integer widths
- DaiPlusPlus 8y agoNull-terminated strings are useful for buffering values of unknown length (e.g. copying from a network stream). Length-prefixed strings have issues of their own (e.g. what size length prefix to use, efficient encoding of variable-length integer prefixes, what happens if a length-prefixed string's calculated end-position is outside the process' memory space?)
- thaumasiotes 8y ago> Null-terminated strings are useful for buffering values of unknown length (e.g. copying from a network stream). That doesn't seem like a good example. The data arriving over the network arrives length-prefixed, because 0 is a legal byte value for arbitrary data. What do you then gain by throwing away your existing knowledge of the length?
- saagarjha 8y ago> null terminated strings This is mentioned. > machine dependent integer widths What exactly do you dislike about this?
- rusk 8y ago> What exactly do you dislike about this? Or what do you even do about it? A handful of solutions already exist: - Use a higher-level language - Java
- Dylan16807 8y ago> Or what do you even do about it? All you need is a list of what width each type is. > A handful of solutions already exist: - Use a higher-level language - Java It doesn't require being "higher level". If anything it pushes code to a slightly lower level.
- skgoa 8y agoI wonder whether the author also hates the 0 and 1 elements of natural numbers. Since they have the same flaw of having weird, special semantics that all other other numbers don't share. In fact 0 is not even a number, but a placeholder for the concept of the absence of a number. Just like NULL.
- BlackFly 8y agoI can assure you that the number 0 in 0℃ is a number and not the absence of temperature. But other than that, excellent point about the wonkiness of special values.
- rusk 8y ago> the number 0 in 0℃ is a number If we're going to get pedantic ... 0 in this case is an offset. The number is what's behind it: The point at which water freezes (aka 273 °K). In this case 0 indicates the absence of any offset.
- paulhilbert 8y agoI wonder why this gets downvoted - seems pretty reasonable to me...
- pjc50 8y agoIt's a rather odd argument and it falls apart as soon as you ask about 0 Kelvin.
- thaumasiotes 8y ago> it falls apart as soon as you ask about 0 Kelvin Huh? The whole point of Kelvins is that their zero point is an actual 0 value. That's why Kelvins are a unit and °C aren't. Q: An object's temperature is 20°C. The object's temperature increases by 10%. What is the new temperature of the object? A: 49°C.
- saagarjha 8y ago> it means that C-strings cannot be used for ASCII or extended ASCII. Instead, they can only be used for the unusual ASCIIZ. This is a very pedantic quibble, and I'm not even sure it's correct. ASCII has NUL as well, and ASCIIZ isn't a character set AFAIK. > C++ NULL boost::optional, from Boost.Optional First of all, nullptr, second, std::optional. > Objective C nil, Nil, NULL, NSNull Maybe, from SVMaybe Nil is not a thing in Objective-C, to my knowledge. > Swift Optional You're looking for nil. So it should be four stars? > Swift’s UnsafePointer must be used with unsafeUnwrap or ! You're confusing Optional and UnsafePointer.
- saurik 8y agoWhat the comment about ASCII means is that NUL is a valid character in an ASCII string, but it can't be represented in C's null-terminated string encoding as the format (sometimes called ASCIIZ, but yeah: not an encoding... but I mean, come on... the article is clear here) terminates at the first NUL. Also, Nil is absolutely a thing in Objective-C: it is a null pointer of type Class (whereas nil is a null pointer of type id; you should avoid mixing them up, though I will admit nothing much bad will happen as Class and id are generally co-polymorphic due to the type system being kind of lame. I am not sure they always have to be, though). (And as someone who has been programming in C++ since before it was standardized at all, I frankly think listing NULL and boost::optional is totally acceptable and complaining about it as if C++11 is more canonical is just being annoying.) Doing a quick search for how nil works in Swift, it apparently isn't a null pointer, so you are wrong there as well :(. > nil means "no value" but is completely distinct in every other sense from Objective-C's nil. > It is assignable only to optional variables. It works with both literals and structs (i.e. it works with stack-based items, not just heap-based items). > Non-optional variables cannot be assigned nil even if they're classes (i.e. they live on the heap). > So it's explicitly not a NULL pointer and not similar to one. It shares the name because it is intended to be used for the same semantic reason. Given that I don't think any of the rest of your comment was legitimate criticism, I am frankly betting that your comment about UnsafePointer is also not useful, but I am kind of tired of having to analyze this comment at this point (I stepped in due to the note about character sets and the floor kept sinking).
- revskill 8y agoThe problem is in tooling. If all compilers/builders out there could detech null for us, those kinds of error could be taken care with much more ease.
- rusk 8y agoA lot of static analysis tools will do this. I can't speak for other IDEs, but Intellij will always warn me if I'm exposing myself to some NULL operations. I do think better first class language support is the way to go though.
- giornogiovanna 8y agoTypeScript can be easily configured to do this[0], and Kotlin always does this[1]. The future is now! [0]: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html https://www.typescriptlang.org/docs/handbook/release-notes/t... [1]: https://kotlinlang.org/docs/reference/null-safety.html https://kotlinlang.org/docs/reference/null-safety.html
- ozim 8y agoIn VS with resharper I get "Possible null exception" as warning, but null exception is runtime thing and not compile time, so I don't see how it is tooling problem. Compiler does not know your user did not fill in some field in form.
- empath75 8y agoA higher level programming language is a kind of tooling. Use a language that doesn't have NULL and problem solved.
- jchw 8y agoThis tidbit gets a ton of mileage but I think it's overrated. There are a lot of unsafe shortcuts we take to get better ergonomics and NULL is one of them. I think it's a bit unlikely we'll fully get rid of null, but we can get rid of some of the pitfalls. TypeScript for example pretty much fixes the problem, by enforcing you check for null when needed, though TypeScript takes a handful of other soundness shortcuts. Go makes null less harmful by treating nil pointers like empty values by convention.
- cyphar 8y agoI 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.
- 8y ago
- bryanrasmussen 8y agopreviously: https://news.ycombinator.com/item?id=11798518 https://news.ycombinator.com/item?id=11798518
- merricksb 8y agoAlso... https://news.ycombinator.com/item?id=10148972 https://news.ycombinator.com/item?id=10148972 (150 points | Aug 31, 2015 | 143 comments)
- bryanrasmussen 8y agojust about nothing is as popular as null is.
- rusk 8y agoNULL is a convenient way to map singularities in your model of the problem. I have on a few occasions tried to write NULL-less code, and it adds a good bit of work. - model all possible states for a value - determine appropriate default actions for all types - meaningful place-holder values It's a good exercise, and I think more code should be written this way, but - as an Engineer I'm trying to model just enough of the problem to solve it. I'm not trying to simulate every possible outcome in that domain. Certain corners of your problem simply don't need to be modeled, and what's more the effort needed to model them can just be too much. NULL is a great way to just throw up your hands and go "I don't know and I don't care". Much as when modelling a physical system singularities typically represent phenomena that the model doesn't take account of, so it goes with NULL. It simply says "Don't Go There".
- knocte 8y agoHave you ever dealt with the Maybe(Haskell)/Option(F#) types? If not, then you don't understand what's wrong with NULL and how to easily avoid it without much work.
- jstimpfle 8y agoI find Maybe a bad idea. It forces me to write denormalized code when I know that something is not NULL. It's not possible to specify this knowledge as a data structure since data structures are static but context is dynamic. I much prefer the simple NULL sentinel that blows up like an assertion when I made a mistake. That said, there's not very often a need for NULL at all if you structure the code correctly.
- funciton 8y ago> I much prefer the simple NULL sentinel that blows up like an assertion when I made a mistake. Haskell, for instance, has the 'fromJust :: Maybe A -> A' function that allows you to do just that. It unpacks the Maybe typed value and throws a runtime error if it fails.
- 8y ago
- AJRF 8y agoDo people generally agree that Java Optional == Scala Maybe / Haskell's Maybe? Java's Optional seems fundamentally flawed in that Java allows any reference to hold a null value and Optional can still throw a NPE when calling isPresent on it so it still gives people a footgun.
- based2 8y agoMaybe Not - Rich Hickey (clojure), 29 nov. 2018 https://www.youtube.com/watch?v=YR5WdGrpoug https://www.youtube.com/watch?v=YR5WdGrpoug https://dotty.epfl.ch/docs/reference/intersection-types.html https://dotty.epfl.ch/docs/reference/intersection-types.html
- fmjrey 8y agoYes I was going to mention that video in which Rich makes an important point in my opinion: database tables, or objects, or structs, still live within the Place Oriented Programming (PLOP) mindset. That mindset was born in a time where disk and RAM were the expensive resource therefore update-in-place was the default. I insist on the "in-place": you need to know where something is so you can update it. The downside of PLOP is that if you have no value for one of the slot in your generic form (be it a table, object or struct) then what can you put there? The alternative is to use data shapes that do not require something to be in a certain place. Hence the use of maps as the most basic data shape: you either have an entry in it, or you don't, no need to have a null entry. Expanding that thinking to databases, and you realise tables is not the right aggregate, instead you need to go one level down to something that datomic calls datom, or RDF calls a fact. To summarise, PLOP forms that package together a set of slots to be filled magnify the issue of NULL/null/nil. Instead make the slot your primary unit of composition and make sure you use aggregation of slots that does not force you to have slots filled with a null value when there is no value in the first place.
- kybernetikos 8y agoYes, 'maybe not' is very relevant to this discussion, but few people seem to agree with my understanding of what he says about the right solution: Optionality doesn't fit in the type system / schema, because it's context dependent. For some functions, one subset of the data is needed, for others a different subset. Trying to mash it into the type system / schema is just fundamentally misguided.
- doubletgl 8y agoIsn't there an inherent need in programming to express an explicit "nothing" value? Coming from Python and JS, I never found None/null to be much of a problem. I in fact like the distinction of null and undefined in JS. Using null allows you to distinguish from the accidental undefined.
- zoul 8y agoThe key word here is explicit. Explicit Maybe/Optional types from Haskell, Swift or Rust are wonderful. After my experience with Elm and Swift, the mere thought of going back to implicit nullability everywhere hurts.
- sitharus 8y agoNo, there’s no inherent need for every variable to allow a nothing. There is a need for a nothing value in many cases and languages without an implicit null, such as Haskell, F# and Rust, employ an ‘option’ type. It’s a dedicated type that either contains a value or nothing. It forces you to declare when you expect a potential null and to check for it.
- deleted 8y ago[deleted]
- doubletgl 8y agoI didn't use the word variable. I think we're talking about the same thing here.
- enriquto 8y ago> Isn't there an inherent need in programming to express an explicit "nothing" value? In numerical calculus: yes, most certainly. What do you expect the result of log(-1) to be? The alternative is to use specially tagged particular numbers as "no-data", and pray so that they do not appear naturally as the result of computations.
- da_chicken 8y ago
- pdkl95 8y ago> NULL is a value that is not a value. And that’s a problem. The problem isn't NULL, it's languages not enforcing the necessary checks for the "no data" condition. Option can still be NULL ("None" in rust), wrapping NULL in a struct doesn't provide any safety. The safety of Option wrapper types is from the other language features (like rust's "match") and a stricter compiler that forces the programmer to write the NULL check. NULL would be fine if C required you to write this: foo_t *maybe_get_foo(/*...*/) { if (/*foo_is_available*/) { return foo; } else { return NULL; } } foo_t *f = maybe_get_foo(); if (!f) { /*...*/ } // REQUIRED or compile error do_something(f->bar); // only allowed after NULL check Obviously implementing that requirement would be difficult in C. Languages like Rust were designed with enforcement features (match + None, much stronger type/borrow checking), but lets your have "a value that is not a value".
- masklinn 8y ago> The problem isn't NULL, it's languages not enforcing the necessary checks for the "no data" condition. Talking about "NULL" pretty much implies that. When Tony Hoare talks about null references, it's about every reference being nullable in languages like Java or C#, not about the ability to conceptually wrap/opt non-nullable references in a nullability thingie.
- danesparza 8y agoPsst: I think the thingie you're referring to is called a 'monad': https://en.wikipedia.org/wiki/Monad_(functional_programming) https://en.wikipedia.org/wiki/Monad_(functional_programming)
- bfrydl 8y agoIt isn't. There exists a common monad that solves this problem but a wrapper type like Option or Maybe need not be a monad. For example, `Nullable<T>` in C# is not a monad.
- 8y ago
- burfog 8y agoLinux does well with some macros: IS_ERR, IS_ERR_VALUE, PTR_ERR, ERR_PTR, PTR_ERR_OR_ZERO, ERR_CAST, IS_ERR_OR_NULL https://elixir.bootlin.com/linux/latest/source/include/linux/err.h https://elixir.bootlin.com/linux/latest/source/include/linux... No, it doesn't totally replace NULL, but it does solve some of the problems in a high-performance way.
- jmfayard 8y agoProgress, it's now a solved problem in modern languages like Rust, Swift or Kotlin See for example: https://kotlinlang.org/docs/reference/null-safety.html https://kotlinlang.org/docs/reference/null-safety.html
- masklinn 8y agoI mean it's been a solved problem since the 70s if not earlier, the problem has always been uptake. And that is not solved e.g. C++ recently introduced std::optional, which is not a type-safe version of a null pointer but is instead a null pointer wrapper for value types.
- cmrdporcupine 8y agoNULL in 'relational' databases in particular is a disaster. Or at least according to the notorious Fabian Pascal. http://www.dbdebunk.com/2017/04/null-value-is-contradiction-in-terms.html http://www.dbdebunk.com/2017/04/null-value-is-contradiction-... Codd never proposed it in his original relational model. For good reason.
- pleasecalllater 8y agoI'm afraid current relational database have little common with the Codd's ideas.
- goto11 8y agoDisagree that it is a disaster. Nullability is explicit in the column type, so it doesn't have the "billion dollar mistake". Furthermore you need to represent missing values somehow if you perform a left join.
- xvzxvzxdfg 8y agoA database "null" is not the same thing as being discussed in the article though. But its so cute that you web devs go all in commenting anyway.
- lucideer 8y agonull is often used in relational databases to be semantically equivalent to "info missing". I get that this use of null isn't ideal. However there are quite a lot of cases where it is semantically equivalent to "not applicable"; while some of these cases can be avoided with a restructure, some can't, and in either case I don't see a compelling argument to do so. e.g. a parent_id column that is null for top-level objects in a hierarchy. Restructuring the database to avoid this seems like moving to an unintuitive paradigm in pursuit of rule-following for its own sake.
- molf 8y agoI think it would be nice if `NOT NULL` was set by default on columns. However there are a lot of legitimate use cases that can't be (practically) solved by restructuring. Data can be incomplete. Maybe only because it is not (yet) known. If NULL values were impossible it would create the need for one additional table with a foreign key relationship for every attribute that can be independently NULL. Sometimes this pattern is a good idea, but I don't think I can be convinced it should be the only possible solution.
- axilmar 8y agoNULL is certainly a mistake, but even more of a mistake is not allowing distinct states in variables. NULL is just another case of a state of a variable. Other states are 1, 15, 0xffffffff, etc. That mainstream languages don't handle this is the worst mistake of the computer industry.
- rusk 8y agoMost languages do. Java will always initialise a non-initialised value to NULL for instance (EDIT or 0 or false for primitives). It's simply a reality of how computers operate that when you allocate a piece of memory (a variable) it will have something in it that you'll need to clear or initialise. In this respect, NULL is doing you a favour.
- zxczxc111 8y agoOnly for field variables. Local variables most definitely have to be initialised by the programmer, otherwise it's a compiler error
- deleted 8y ago[deleted]
- david04 8y agoScala alternative to null: https://www.scala-academy.com/tutorials/scala-options-tutorial https://www.scala-academy.com/tutorials/scala-options-tutori...
- captainmuon 8y agoI've made my peace with null. Null is basically just an implicit assert(valid(x)) before every time you call a method on x. Similary, I think of exceptions as explicit "crash-unless-caught" commands. If you write your program with the "blow up early" mentality anway, or use static checking tools and a bit of discipline, I've found that null looses it's terror.
- pjmlp 8y agoThat is a very big IF, considering how many teams are actually structured.
- lawn 8y agoOh I'm fairly disciplined. But we use a 15 year old code base which should've been made with more discipline than it was. Additionally the program shouldn't "blow up early" but it was in many ways coded that way.
- gre 8y ago“Looses its terror” here means the opposite of “loses its terror”.
- piyh 8y agoCry havoc and let loose the null pointer exceptions
- kzrdude 8y agoIf it's C, it's far more terrifying than that.. there is no assertion! just a vauge threat that something will go wrong if you stick a null in there, with no guarantees and no checks.
- deleted 8y ago[deleted]
- lmm 8y agoSure, null can be a non-problem for the low, low price of 3 or 4 extra lines on each function. But those extra lines distort your program architecture, pulling you away (perhaps without even noticing) from short, composable functions.
- userbinator 8y agoTo someone who has been using Asm and C for decades, these arguments just make no sense. Reading this article reminds me of the arguments against pointers, another thing that's frequently criticised by those who don't actually understand how computers work and try to "solve" problems by merely slathering everything in thicker and thicker layers of leaky abstraction. It's not far from "goto considered harmful" either. any reference can be null, and calling a method on null produces a NullPointerException. ...which immediately tells you to go fix the code. There are many times when it doesn’t make sense to have a null. Unfortunately, if the language permits anything to be null, well, anything can be null. That's not an argument. See above. 3. NULL is a special-case ...because it indicates the absence of a value, which is a special case. though it throws a NullPointerException when run. ...and the cause is obvious. I'm not even a regular Java user (and don't much like the language myself, but for other reasons) and I know the difference between the Boxed types and the regular ones. NULL is difficult to debug Seriously? A "nullpo crash" is one of the more trivial things to debug, because it's very distinctive and makes it easy to trace the value back (0 stands out; other addresses, not so much.) What's actually hard to debug? Extraneous null checks that silently cause failures elsewhere. The proposed "solution" is straightforward, but if you reserve the special null value to indicate absence then you can make do with just one value instead of a pair, of which half the time half of the value is completely useless. If you can check for absence/null, you will have no problems using Maybe/Optional. If you can't, Maybe/Optional won't help you anyway --- because it's ultimately the same thing, using a value without checking for its absence.
- hesselink 8y agoThe thing is, you're focusing on when you've detected that there is an issue (a crash). A lot of the issues with NULL are the fact that you can't easily detect if beforehand. It's not indicated in the types, or the syntax. That means that it's incredibly easy for a NULL issue to sneak into an uncommon branch or scenario, only to be hit in production.
- nsajko 8y agoBut why was the scenario not tested before production? Should that not be the case anyway?
- tarkin2 8y agoI've been using kotlin and swift. They've partly removed null with the 'maybe' feature. So instead of calling methods on null objects the methods are just not called if the object is null. This helps when there's a race condition, and you attempt to call a method on a null object, and then that solves itself by the same code being called again without the race condition. But a lot of the time if the object is null and the method is not called you still have an error, but it's just not a null pointer error now. This 'nullless' code is nice in some places, especially with UI lifecycles calling code repeatedly, but other times it just changes the type of error you debug.
- jillesvangurp 8y agoActually in Kotlin, nullability is part of the type system and denoted with a ?. So String and String? are two different types. Dereferencing a nullable type is a compile error until you do the null check. Doing that triggers a smart cast to the non nulled type. So the inferred type becomes non nullable and you don't have to do any casts. You can force this cast by using the operator !!, which you should avoid for obvious reasons but is useful with some legacy code. If you get this wrong you still get an npe. It also provides backward compatibility with java where java types are considered nullable by default unless other wise annotated with a @NonNull. Also you get nice warnings about redundant null checks. This provides for a lot of extra compile time safety and it largely removes the need for Maybe, Optional, and other kludges that people have been coming up with to force programmers to replace null checks with empty checks.
- sifoobar 8y agoNah. There will always be missing values, no matter how many layers of safety measures we wrap around the fact. Hitting a NULL in C is very unforgiving; but that's just the spirit of C, there are plenty of ways to provide a less bumpy ride in higher level languages. My own baby, Snigl [0], uses the type system to trap runaway missing values without wrapping. Which means that you get an error as soon as you pass a NULL, rather than way down the call stack when it's used. https://gitlab.com/sifoo/snigl#types https://gitlab.com/sifoo/snigl#types
- bunderbunder 8y agoYou know who works on a platform with NULL but doesn't have quite so many problems with it? DBAs. There's some need to draw a distinction between the basic idea of NULL, and the way that NULL has been implemented in most high-level programming languages. In most RDBMSes, values can't be null unless you say they are. Sometimes explicitly, as in table definitions, sometimes implicitly, when you select a JOIN type. Either way, though, the fact that the developer is in control of when it can and cannot happen means that it always has a knowable meaning. (Or should, anyway.) The problem with many programming languages is, you're given it whether you want it or not. In a low-level language like C, that's reasonable, because it takes a sensible approach to how it works: Only pointers can be null, and all pointers are nullable for obvious (especially in the 1970s) reasons. More generally, I'm not going to fault languages from that era for trying it out, because this stuff was new, and things were still being felt out. So I don't really fault Tony Hoare for giving null references a try in ALGOL W. What seems much more bothersome is high level languages like Java and C# cargo culting this behavior. They could have followed the lead from languages like SQL and let the programmer be in control. They should have. They already throw exceptions when a memory allocation fails, and they allow inline variable initialization, and declaring variables at the point of usage, and composite data types have constructors, so they lack all of (early) C's reasons why ubiquitous nulls were a good idea. They could have, I think quite easily, made nullability optional. At which point it'd have basically the same semantics as optional types from functional programming, so I doubt we'd be worrying about it anymore. But they didn't.
- lmm 8y agoNULL in SQL is a notorious source of errors and confusion (particularly when it comes to e.g. tri-state boolean logic). It certainly can come from nowhere and surprise you - if anything the behaviour is even worse than in Java or C#. So I don't think there's anything to learn from there. (Rather what modern languages should have done - and increasingly do - is follow ML practice and avoid null entirely, implementing option types as ordinary library types where the programmer explicitly wants to represent absence).
- bunderbunder 8y ago
- porpoisely 8y agoNULL can mean and be different things in different domains of computer science. NULL in the database world isn't the same thing in the programming world. In the programming world, null is a result of the system architecture, systems programming, etc. In SQL, NULL is a result "lack of data". There have been debates on whether there should be different types of NULL. A NULL type for "data that is available but we don't have it yet" - like car make and model for a car owner. A NULL type for "data that does not apply" - like car make and model for an adult who doesn't own a car. A NULL type for "data that never applies" - like car make and model for a child. Then you get into the philosophical debate on whether a NULL can ever equal a NULL. Does it even make sense to even think of NULL in terms of equality. How can an unknown entity ever equal another equality? But what if you are just asking "are they both unknown"? Then you probably can think of two NULLs as "equal". In higher level programming, the consensus seems to be the less nulls the better. Which is why languages like C++, C#, etc are introducing Option-like syntax ( mostly to accommodate the database world and their NULLs ). NULL exists to solve particular problems in computer science. It can also cause a lot of problems. You can argue it's the best solution and worst mistake depending on the situation.
- lalaithion 8y agoAll of those different nulls can be solved by not having null as a special case of your database specification, but as a first class type construct. data MightBeData a = Yes a | Unavailable | NotApplicable | NeverApplicable
- Twisell 8y agoNULL in databases have many properties that save a shitload of coding time and help write more secure code. To cite only one of theses useful properties NULL automatically propagate through all operations and aggregations.
- eksemplar 8y agoI think you can say something similar about programming languages that don't have automatic memory assignment. It can be perfectly valid to have a pointer reserved in memory, without having given it a value to point to yet. I see why this isn't much of an issue in 2015, but it was. I don't think NULL is a problem at all really, but it sure isn't fun in languages like C# where you can have nullable types, but I think that's a problem with C# and not the concept of NULL.
- sharpercoder 8y agoYet `Maybe<T> | Option<T> | ...` is not an option (pun intended), as Rich Hickey explains here: https://www.youtube.com/watch?v=YR5WdGrpoug https://www.youtube.com/watch?v=YR5WdGrpoug. In effect, his argument is: 1) You have `public X Do(Y y)` changed into `public X Do(Option<Y> y)`. This will break your API. 2) You have `public X Do(Y y)` changed into `Option<X> Do(Y y)`. This will break your API. Thus, do not use Option<T> or equivalent in your API's. Only use a language-supported construct such as C#8's upcoming `string?` and `string`.
- bunderbunder 8y agoThis is a spot where I've got to respectfully disagree with Mr. Hickey. Changing a public API call that used to guarantee that it returned a value so that it might now return nothing is a breaking change, and, as an API consumer, I want my APIs to broadcast that change loudly. Compiler errors are a good (but not the only) way to do that. Changing a public API member so that its arguments are now `Maybe[T]` is just silly. There's no need to introduce a breaking change there. Just overload it so that you now have versions that do and do not take the argument and get on with life. If there's an argument to be made here, it's that statically and dynamically typed languages require different ways of doing things. In a statically typed language, I expect the compiler to keep an eye on a lot of these things, and I'm used to leaning on the compiler to catch things like a function's return value changing. In a dynamic language, I'm not. I'm also, when working in a dynamic language, used to having to deal with the possibility that, at all times, any variable could contain data of literally any type. Removing nullability there changes the set of possible "this reference does not refer to what I expected" situations from (excuse the hand waving) a set with infinite cardinality to a set whose cardinality is infinity minus 1. If you think of NULL as effectively being a special type with a single value (call it "void"), then eliminating it reduces the number of classes of errors I have to worry about in a dynamic language by 0. I'm hard pressed to see any value there.
- tatut 8y agoThis is backwards. Rich did not advocate for changes that break promises. The point in the talk is that "strengthening a promise" should not be a breaking change. Changing return type from "T or NULL" to always returning T. The case where you previously couldn't guarantee a result, but now you can. The other case "relaxing a requirement" also should not be a problem. The case where you previously had to give me a value, but now I don't need it and can do my calculation without it.
- MichaelMoser123 8y agoChecking if the optional is present is very similar to checking for NULL values. Now if you have a nice match statement like rust and lambda functions for streams, that may make things a bit more readable. You will still need analysis tools to check that all code paths check for none before accessing that value ..
- deleted 8y ago[deleted]
- cphoover 8y agoOption monad fixes null reference problems. Does not fix the ambiguity between not found vs doesn't exist.
- lmm 8y agoIf you have multiple possible reasons for a value to be "absent", that sounds like a case for an "either" or "result" type.
- vectorEQ 8y agoin C NULL is just 0. a nullptr in c++ is just a pointer which points to 0. so it's not an undefined value... it's set to 0 on purpose so you can check it. consider this: char ptr; ptr = (char )0xb8000; before assigning ptr, ptr can be ANY value from 'random memory'. (compiler trickery aside.. because it might initialise it to 0 anyway...) so you want to have: char ptr = NULL; ptr = (char )0xb8000; So you can then do IF(ptr != NULL) { do_stuff(); } you could not check for validity of the ptr value or it being present otherwise. an if(ptr) or if(!ptr) would only work if it's initialised and reset to NULL each time before assignment, so you can validate the assignment. This is not mistake but a tool. for a hardcoded offset like this it might be fair to say you could do if(ptr == 0xb8000) {do_stuff()} but what if it was a ptr returned by a new allocation or so? Or by taking the address of another variable or object? In that case setting things to null and checking them is absolutely essential to assuring your code works like you intended it to. this whole article seems just a bunch of nonsense. for some languages it might hold true, but i can't beleive it would do for any. perhaps this original algol null... who knows.
- pjc50 8y agoProper typesafe systems wouldn't let you use C-style reinterpretation casts either. It's quite instructive to see how the low-level Rust people handle this.
- AnimalMuppet 8y agoSo if you have memory-mapped IO, how would you write to a specific address? In C/C++, a reinterpret cast is exactly what you need there. What would you use in Rust?
- pjc50 8y agoThere are various alternatives, but generally the approach is to know what address you need in advance and create an object for it at compile time that can be accessed type-safely. e.g. https://zinc.rs/apidocs/ioreg/index.html https://zinc.rs/apidocs/ioreg/index.html or the longer but more detailed http://blog.japaric.io/brave-new-io/ http://blog.japaric.io/brave-new-io/ , which covers various approaches. It even points out that you can use the type system to enforce that peripherals are only accessed from multiple threads or parts of the program in a safe manner, which you can't do if you can just reinterpret-cast into anything.
- nayuki 8y agoI agree with pretty much everything in the article. However, I would give Java a lower score because no one uses java.lang.Optional in practice, and there is too much legacy libraries and application code that cannot or will not be changed. Also, the @NotNull annotation isn't in Java SE; it is made available through various third-party libraries. A language with a null value can dramatically simplify things for a language designer, though. In the case of Java, we know that every array of objects is initialized to null references. Thereafter, we can construct and assign objects to each slot of the array. Otherwise we run into issues that C++ faces - when we construct the array, the field of every object is uninitialized, so they are potentially dangerous if read or destructed, and need the special syntax of placement new to be initialized. The trick to avoiding null here is to avoid pre-allocating an array, and instead to grow a vector one element at a time. The C++ std::vector<E> is very accessible and performant, whereas Java java.util.List<E> is very clunky to use compared to native arrays. Another case that gets simplified is object construction. When the memory for an object has been allocated but before the user's constructor code has run, what values should the fields have, assuming that they are observable? In a Java constructor, all fields are initially set to null/0, then you simply assign values to fields in the body code of the constructor. In C++ constructors however, you should initialize fields in the initializer list, and then you have still have the option to initialize fields in the body. I still think pervasive null values are bad for the programmer (rather than the language designer). Now that I have preliminary experience in Rust, I see that its design is much safer and still practical, so I think this language shows the way forward.
- arcticbull 8y agoRe: Array Initialization One approach you can take is the Rusty "hang up a technical difficulties sign" (unsafe) while you mess around with potentially uninitialized memory, which is valid, but places the burden on you as the library writer. Another would be to initialize your array of pointers as an array of Option<Box<T>> pre-filled with None. Due to pointer alignment you can actually optimize Option<Box<T>> by turning it into a tagged pointer (which I believe is what Rust does) so that None == null at the machine level, while the language exposes a safe interface on top. [1] Re: Object Construction With object construction in Rust you can either (a) create all fields in advance and specify them at construction [best] (b) use mem::uninitialized() [bad] or (c) create a builder which has optional fields for everything and yields a constructed option via 'a' later [most work]. [1] https://doc.rust-lang.org/std/option/ https://doc.rust-lang.org/std/option/
- pella 8y agoJulia Missing Values "Julia provides support for representing missing values in the statistical sense, that is for situations where no value is available for a variable in an observation, but a valid value theoretically exists. Missing values are represented via the missing object, which is the singleton instance of the type Missing. missing is equivalent to NULL in SQL and NA in R, and behaves like them in most situations." https://docs.julialang.org/en/v1/manual/missing/index.html https://docs.julialang.org/en/v1/manual/missing/index.html + "First-Class Statistical Missing Values Support in Julia 0.7" https://julialang.org/blog/2018/06/missing https://julialang.org/blog/2018/06/missing
- ChrisRackauckas 8y agoThe nice thing about Julia is that it separates `nothing` from `missing`. nothing<:Nothing is a null that does not propagate, i.e. `1+nothing` is an error. It's an engineering null, providing a type of null where you don't want to silently continue if doing something bad. On the other hand, missing is propagates, so `1+missing` outputs a missing. This is a Data Scientist's null, where you want to calculate as much as possible and see what results are directly known given the data you have. The two are different concepts and when conflated it makes computing more difficult. By separating the two, Julia handles both domains quite elegantly.
- tabtab 8y agoFrom my practical experience, Null has a use, but is over-used or misused. If you concatenate a Null string to other non-Null strings, the Null should usually be treated like a zero-length string instead of nullifying the ENTIRE expression result. I know this differs from how numbers typically are treated, but so be it. Strings are not numbers. Without that behavior, one often has to write verbose statements such as: denull(stringA,"") || denull(stringB,"") || denull(stringC,"") || denull(stringD,"") etc. ("denull" name varies per vendor. "||" is concatenate here.)
- zyxzevn 8y agoNo. Undefined behaviour is.
- prmph 8y agoIt is not possible to have a NULL type that works for all situations and has stable semantics. The issue is, NULL should be a concept, not a value. I see no problem with using sentinel values, so long as they are well designed, and such good design comes with skill and experience, just as with all other aspects of architecture. The quest to have a single value that can be used for all the various possible meanings of NULL, to me, is the root of the problem.
- lisper 8y ago> The quest to have a single value that can be used for all the various possible meanings of NULL, to me, is the root of the problem. Exactly right. In particular, the conflation of nulls to indicate both error and non-error conditions (e.g. out-of-memory vs end-of-linked list) makes it impossible to distinguish errors from non-errors in many situations, and that is obviously bad. Ideally you want nulls/sentinels that carry information about where, when, and why they were generated. You want separate nulls for numerical overflow/underflow, end-of-linked-list, out-of-memory, timeout, suppressed error/exception, unpecified/unknown value (preferably a separate one for each type) yada yada yada.
- trophycase 8y agoI don't write business software that needs many 9s of uptime so I find null to be fine. Yes, returning null is kind of throwing your hands up but I find that to be kind of the point. It allows my software to fail fast if it does and makes it incredibly obvious where things are going wrong. Generally if a reference has a value of null where it shouldn't, I can pinpoint the location of the bug within a few minutes or even seconds. IMO it makes the program much easier to reason about compared to returning some sort of empty value and then failing much much later in the program.
- jonstaab 8y agoThanks for the article! I've often heard that null is bad, but haven't ever seen such a thorough, readable explanation. Just so I can think it fully through for myself, it seems that the problems with null are: 1. Its semantics are different from whatever type it is substituted for, so can't be used as a value 2. Superficially, it looks identical to a missing record value. This difference might be something you want to ignore (isNullOrEmpty), or something you care about (cache miss or hit with null) 3. It is used both for missing data, and missing functionality, which confuses two separate systems. I agree that null as a type generally works better than null as a value, but I don't know if you can always articulate it as a type, especially in dynamic languages. A pragmatic solution seems to be a combination of: - A Maybe type or monad. This forces you to unpack the nullable semantics of the thing, either in the type system or by unwrapping the value. A Maybe monad is a well designed interface for dealing with the edge cases, but it doesn't make the edge cases go away. This eliminates problem #1, and manages problem #2. - Nil punning. (concat nil nil) yields an empty list in clojure. Same for +, string/join, etc. This is really similar to Monads/Types, but switches the responsibility for handling null intelligently from the data structure to the standard library. Putting null in the type forces you to opt in to null; nil punning forces you to opt out. This makes for more terse code, which is nice, but probably has a slightly narrower scope of application than monads, since it tackles problem #1 by making it make sense in most cases rather than eliminating it entirely, and nil punning doesn't always make sense. Incidentally, this seems closest to PHP's and javascript's strategy; their real problem is that they extend nil punning to cases where nil isn't involved (1 + '1' anyone?). - Key or attribute errors. This is sort of a fallback to compensate for failing to handle the null case, but often works well when something just "shouldn't be null". This is probably just a substitute for a lack of compiler checks, but works well enough in the python world; sometimes failing hard is the right thing. - Distinction between code and data. I like higher-order functions, so I'll just say that "sometimes data includes functions". But in most cases, the function you're calling should be resolved at compile time. Interfaces should be fully implemented, and (as in python), there should be a distinction between missing functionality (AttributeError) and missing data (KeyError). Ultimately, it seems to be a question of language/api/user interface design: there is a difference between present, present and empty, and absent. Regardless of what strategy you use to manage the difference, there has to be one.
- draw_down 8y agoSince it's so close to this topic, I recommend Rich Hickey's talk from about a week ago, called "Maybe Not". It's mostly about the issue of how to specify the shape of data a function requires (takes as input) and provides (emits as output), but it also contains a really nice discussion of problems with Option/Maybe types. Anyway, my feeling is that I don't understand what people expect to happen here. Due to a misunderstanding, bad assumption, or other error, you expected to have something when you actually don't have the thing. A null-reference exception (or equivalent) is just the symptom. Null isn't the problem, our dumb brains that constantly make dumb mistakes are the problem. Option/Maybe does force you to deal with the case that you didn't get anything when you asked for something. But as Hickey points out in his talk, it isn't all benefit, there is a cost, and nobody ever talks about the cost. When I provide you an id and ask for a Book object in return, I want a Book back, not a `Maybe Book`. This concept does not map to anything in the real world, and it has problems in practical usage as well. (e.g. My function changes from maybe giving you a Book or maybe not, to definitely providing you a Book. So I change its return type from `Maybe Book` to `Book`. Except, oops, I just broke every single caller of my function, even though I strengthened the guarantee of the data my function will provide. That shouldn't break anyone.) https://www.youtube.com/watch?v=YR5WdGrpoug https://www.youtube.com/watch?v=YR5WdGrpoug (Discussion of Option/Maybe starts 6:50)
- btbuildem 8y agoAuthor forgot about Erlang, and it's wonderful lack of NULL
- yxhuvud 8y agoHonestly, I feel that the problem isn't null but that type systems (at least earlier on) tended to allow other types to be null, willy-nilly. Null is best considered a separate type to non-null values, and is basically not a problem if the type system handles that in some way. Be it option or union types - both solve it and it mostly stops being an issue.
- bitL 8y agoIf you have self-discipline and aren't lazy, NULL is totally fine. Maybe language designers could have made it behaving more like a 0-dimensional sentinel for beginners.
- gambler 8y agoRich Hickey's "Maybe Not" should be watched by anyone who thinks nulls/nils/undefineds are okay. It should also be watched by anyone who think that Optional/Maybe/Nullable and good enough: https://www.youtube.com/watch?v=YR5WdGrpoug https://www.youtube.com/watch?v=YR5WdGrpoug
- shittyadmin 8y agoConflating NULL with NUL terminated strings seems a stretch... both have problems, but separate problems. I suppose they're both related in that they provide a "special" value rather than separating that information though.
- mynameishere 8y agoI don't care. java.lang.Optional is a PITA in a way null never is.
- sfilargi 8y agoThe problem is not the NULL but the lack of types.
- beardyw 8y agoThat 'nothing' is inconvenient applies the same in mathematics with zero. Why do we have to have a number we can't divide by? What is 0 to the power of 0? It's a special case we always need to worry about. But its inclusion in the number system is not in question. And I remember my distress using a financial package being told that my unused zero value still MUST have a currency! My pocket is empty, how can it have a currency? If a farmers field is empty - must I say what it is empty of - cows, sheep, aardvarks? I think worrying about inconsistency here is worrying about the inconsistency of the world we live in. 'Nothing' is a mysterious thing we need to accept and respect.