8 ms·
What is wrong with NULL
- greggyb 11y agoNo mention of SQL, where NULL's behavior in the standard can lead to some quirky behavior that I've seen bite back in poorly designed systems. I've included a brief illustrative example. The expectation is that the UNION of two WHERE clauses, one using IN and the other using NOT IN should be equivalent to the same SELECT without any WHERE: WITH NullCTE AS (SELECT a.* FROM (VALUES (NULL), (1), (2)) a (Number) ) ,One AS (SELECT Number = 1) SELECT * FROM NullCTE nc WHERE nc.Number NOT IN (SELECT Number FROM One) UNION SELECT * FROM NullCTE nc WHERE nc.Number IN (SELECT Number FROM One) Running this will give you back a two-row table, containing 1 and 2, but the NULL is excluded from both WHERE conditions. The naive expectation is that the combination of a condition and the NOT of that condition cover all possible circumstances.
- paulddraper 11y agoI thought of including SQL NULL. Unfortunately, I felt I would do such a poor job of enumerating the problems or describing them in a somewhat comprehensive way, I didn't even try.
- kazinator 11y agoThe mistake is an unsafe null: making every object type carry a value in its domain which says "Oops, though my type says I'm a Foobar, I'm not actually an object, la la la! Have a free exception on me, in your face!" Lisp's NIL is brilliant. It's in its own type, the null type! And it's the only instance of that type. No other type has a null instance. Null references in the language simply reflect the tension between the static type system which is supposed to assure us that if static checks pass, then values are not misused, and the need to have sometimes (at the very least) a "Maybe" type: maybe we have an object, or maybe not. Null references are the "poor hacker's Maybe": let's make every type support a null value, so that Maybe shows up everywhere. Maybe, or something like the Lisp NIL, are themselves not mistakes by any stretch of the imagination. Sometimes you really need the code to express the idea that an object isn't there: directly, not with hacks like some representative instance, and some booelan flag which says "that's just a decoy indicating the object isn't there". You want the exception if the decoy is used as if it were an object; that's a bug. Something is trying to operate on an object in a situation when there is no object; safely operating on a decoy just sweeps this under the rug. (Yet, not always: sometimes operating on the decoy is fine. Lisp's NIL lets us specialize a method parameter to the NULL class and deal with it that way in one place.)
- xyzzyz 11y agonil really isn't any better than null-pointers/references. In the end, you need to check for nil-ness anyway, and there's no mechanism that will help you ensure the check is made. Algebraic data types with pattern matching, that is, Maybe/option is really the proper solution.
- kazinator 11y agoNIL is objectively, unquestionably safer than a raw machine pointer that is null. It's a first class object with a type; a symbol with a name, and instance of the NULL class and so on. Of course, if you want to use NIL as a character string or floating-point value, there is an exception. That's your fault: you needed a way to indicate "I don't have a string here" or "I don't have a number", and you bungled the logic.
- mcguire 11y ago"Of course, if you want to use NIL as a character string or floating-point value, there is an exception. That's your fault: you needed a way to indicate "I don't have a string here" or "I don't have a number", and you bungled the logic." And there's the rub: Just like NULL. And, in fact, that's the major problem with NULL. NIL is better than NULL, but not by much.
- pmelendez 11y ago> NIL is better than NULL, but not by much. NIL is probably the best you can get once you realize that the empty state is unavoidable. My personal problem with this kind of rant is that is full of hate but lacking in general in alternatives to represent an empty state. The author mention optional types as an alternative. That's fine for parameters but that doesn't cover when you need to have an empty reference.
- weaksauce 11y agoruby nil is similar in that it's an instance of the Nil class. though, calling a method on nil that isn't there is a common error. I like how swift handles things. strong type system that makes it a little painful to do things unsafely. guarantees some things that make it a powerful and more safe language than objective c. I think they lost a few dynamic patterns though in the transition but that's just what i recall from a blog post and the details of it are a little fuzzy.
- PaulAJ 11y agoThis mistake is fixed in Haskell.
- seagreen 11y agoThat's mentioned in the article. It gives Haskell's null handling a score of 5 stars, along with OCaml and Standard ML. EDIT: The articles definition of 5 star null handling is "Does not have NULL." :)
- deleted 11y ago[deleted]
- kinghajj 11y agoRust, too! Edit: Btw, I disagree with how the article categorizes Rust in comparison to Haskell. It shows that Rust has std::ptr::null, but neglects the fact that Haskell has Foreign.Ptr.nullPtr. Either both should be "5 stars" or both should be "4 stars".
- bbatha 11y agoYa, all of the 4 star ones basically include it in their ffi compatibility layers. The five star ones do too.
- lambda 11y agoCame here to say this. In fact, I think every language that they give 5 stars to has some form of "foreign pointer", "raw pointer", "unsafe pointer" or the like that is nullable, for FFI and other low level tasks. I think that anything which has no null in normal, idiomatic code, outside of "unsafe", "ffi", or similar subsets, should get 5 stars. The distinction is really about whether you need to worry about any possible value, or any possible reference, being null, which you do in languages like C or Java where all references are nullable. Giving Java and Rust the same 4 star rating because they both have some form of null and some form of Maybe/Option is a bit misleading. In Rust, Option is what you use in any normal code, and so you don't need to check for Null everywhere. In Java, it's the other way around; nullable references have existed since the beginning and are not segregated in any particular way, while Optional is a recent addition. Likewise, I think that in Scala and Swift, null is only present for compatibility purposes, and idiomatic code does not use them. I'm not sure about F#. Clojure does use nil idiomatically, and also has '(), which may even count as "multiple NULLs" according to this rubric, though I guess that in Clojure '() is just treated as an empty list, rather than a null value like it is in other Lisps.
- ant6n 11y agoFor statically typed languages this is definitely an issue, but for dynamic languages less so. In Python, I wouldn't use an optional value, x is None seems to be just fine. I'm still waiting for std::optional for C++.
- lmm 11y agoIt's only not an issue in dynamic languages in the sense that you have the same issue anyway without null because you have no type safety at all.
- kazinator 11y agoYou have a ton of type safety in Python, which is one reason why Python programmers are cranking out large bodies of working code representing all kinds of applications that Just Work. "No type safety at all" means you can misuse a value of one type using an operation that is appropriate for a different type, and some nonsensical behavior silently occurs (or perhaps some nonportable behavior that you sneakily intended). This characterizes assembly languages, certain machine-oriented languages like BCPL, and some immature dynamically typed languages which omit run-time-type checks (the type information is there, but not checked, so that string_length happily tries to operate on an integer, so that a type problem occurs in the interpreter's kernel itself.)
- lmm 11y agoPython has some safety checks that are missing in other languages, and this is a good thing. But it's misleading to call them "type safety", because they aren't connected to anything that can be meaningfully called a type system - all those checks happen at the value level (e.g. you can make a particular integer amenable to string_length, by monkeypatching the relevant methods onto it).
- astral303 11y agoboost::optional and call it a day. http://www.boost.org/doc/libs/1_59_0/libs/optional/doc/html/index.html http://www.boost.org/doc/libs/1_59_0/libs/optional/doc/html/...
- bronson 11y agoUglier than a Windows backslash, odder than ===, more common than PHP, more unfortunate than CORS, more disappointing than Java generics, more inconsistent than XMLHttpRequest, more confusing than a C preprocessor, flakier than MongoDB, and more regrettable than UTF-16, the worst mistake in computer science was introduced in 1965. That could be the greatest intro sentence ever seen on Hacker News.
- protonfish 11y agoPretty good, but I've never had a problem with an XMLHttpRequest being inconsistent. Everything else seems spot-on though.
- iron_ball 11y agoI think they're joking about the capitalization of the name.
- paulddraper 11y agoYeah. That's what I was going for ;)
- protonfish 11y agoI get it now! Duh.
- vbezhenar 11y agoXML is uppercase but Http is not. That seems to be inconsistent, though in some coding standards that's a correct naming scheme: acronyms with length less that 4 are named in uppercase (XML, URL) and acronyms with length >= 4 are named as Http. Another example is HttpURLConnection class from the standard Java library.
- choult 11y agoClearly you've only started using it in the past few years... It was horrific and totally inconsistent across browsers and between versions about 8 years ago and earlier.
- SamReidHughes 11y agoNULL is okay as long as you pretend it doesn't exist. I mean, in these languages, uninitialized variables exist at some point (fields start uninitialized in constructor bodies, etc), and that's why there's null, instead of defining them with some garbage value that has undefined behavior. But the right solution for users is to just pretend that it can't exist, and that uninitialized variables have a garbage value. Of course, that's idealistic, most languages don't have some Option<T> type without a performance penalty, and pre-existing libraries exist. (Usually you should design around needing Option<T> too.) The right language design decision for these languages (managed languages like Java) might have been to make uninitialized references have a garbage value that reliably throws an exception when used (i.e. null) -- you can copy the reference and pass it around, but you can't compare it for equality and any attempt to inspect its value results in an exception.
- MichaelGG 11y agoYou can pretend all you want, and sure if you force yourself to always use Option then many problems will go away. It still is a huge hole in your type checking and will still cause mistakes to occur.
- SamReidHughes 11y agoIt's not a huge hole and not such a big deal. Just don't design APIs to use null. I've done it, and I can tell, you, it works! (But to be fair, I haven't worked on database-backed software in half a decade.) When mistakes do occur they're the trivial kind where you get a stack trace and can work it out. It's wrong to call null pointers a billion dollar mistake, too -- if we didn't have null (and in the absence of generics that permit Option, as things were long ago), programmers would end up using in-band sentinel values instead. That would be a much worse mistake.
- paulddraper 11y ago(1) It's true that a sentinel value like NULL can cut cycles. That's low-level code though, and not high-level code. I'd suggest that most code is (should be high level). (2) It is true that you may have uninitialized values. There are several ways to address it. Java will not allow you to use uninitialized local variables. C/C++ leaves the values of some uninitialized variables undefined. There are several possible approaches. Treating null as a normal thing is not a good approach, though.
- SmokyBourbon 11y agoIt only seems like a mistake in an age where there's practically unlimited memory available.
- aurelian15 11y agoI agree with the article, yet, despite what the table at the end suggest I got significantly less problems with NullPointerExceptions since I switched from Java to C++ for my work. C++ has an inbuilt alternative: references. References in conjunction with the Null Object Pattern have solved my problems until now. References simply cannot be null -- and I generally do not use raw pointers in my code unless some library forces me to.
- _yosefk 11y agoA null pointer can be dereferenced into a "null reference" which can be passed around, however. Theoretically UB, in practice, typically represented identically to the null pointer and behaves the same when used. Still helps in that the null bug root cause can hide in less places (modulo memory overwriting which adds root causes unimaginable in Java)
- deleted 11y ago[deleted]
- MichaelGG 11y agoYou don't need multiple return for this - that's exactly what Maybe is for. (You should have multiple return via tuples, but for other reasons.)
- feedjoelpie 11y agoAnother reason that, even despite sharing a lot of syntax with PHP, Hack is one of my favorite languages: http://docs.hhvm.com/manual/en/hack.nullable.php http://docs.hhvm.com/manual/en/hack.nullable.php
- vbezhenar 11y agoMany modern languages (Kotlin, Rust, Swift) handle this problem well. Though I'm not sure if that problem is really that huge. Bad code will break in many ways. And breaking on nulls usually isn't that dangerous, data isn't corrupted and stack is safe. There's another mistake of computer science in my opinion is inefficient array bounds checking and implicit integer overflow behavior. And those mistakes are more dangerous, they lead to data corruption and exploits.
- carsongross 11y agoWe don't need to get rid of null, we need more, type-specific and context-carrying nulls: types need a way to signal various error states via an enumeration of instance constants that carry the error state in a type-compatible manner without some syntactically crappy mechanism like Maybe. class Connection { //Error instances BAD_ADDR("The given address was not correct...") ... } These constants should throw, just like a null value would, but with the specific error-state message, and should be testable for both the general and specific cases. Try/catch has its place, but the persistence of null-as-error-state behavior among programmers shows that signalling-via-return-value is a practical and intuitive way to handle error states. We should make that better, rather than simply tipping our nose at null.
- dragonwriter 11y ago> We don't need to get rid of null, we need more, type-specific and context-carrying nulls: types need a way to signal various error states via an enumeration of instance constants that carry the error state in a type-compatible manner without some syntactically crappy mechanism like Maybe. Sounds like you want to go a step beyond Maybe to Either.
- geofft 11y agoIf you have a language with enum types (Haskell, ML, etc., or any of the languages inspired by them like Rust, Scala, etc.), that's straightforward. In e.g. Rust, you'd have enum Connection { ValidConnection {fd: RawFd, address: SockAddr, ...}, BadAddr, BadPort, ... } In fact Rust's representation of the maybe type is just a generic enum Option<T> { Some(T), None, } so all you're doing is getting rid of the two layer Option<Connection> syntax and making it a single Connection, so you're not writing Option<this>, PossiblyBadAddress<that>, etc. everywhere. Then all your call sites can do one of two things. Either they can check for specific errors, like match conn { Connection {fd, address, ..} => write(fd, ...), BadAddr => /* handle error */, BadPort => /* handle error */, } or you can write a simple function that throws an exception, if you really want an exception-handling style: impl Connection { fn get_fd(&self) -> RawFd { match *self { ValidConnection {fd, ..} => fd, BadAddr => panic!("The given address was not correct"), BadPort => panic!("The given port was not correct"), } } } and then call conn.get_fd(). For a simple command-line app, panicking and aborting is pretty much what you'd want to do anyway. For a test suite, panics are caught per test case (and you can even test that a function does panic with a given message), so it's very testable.
- geofft 11y agoIt's worth noting that having a NULL, somewhere, is not so much a problem as forcing types to have a NULL. In this respect, e.g. Python and Rust both get it right: while Python has a None, and Rust has std::ptr::null(), an object that is a string cannot be any of those, because those are different types (NoneType and raw pointer, respectively). C's problem is that a string could be null, and there's no syntax for a non-null string. Python's problem, meanwhile, is that any parameter could be any type. In C, you can pass NULL to a function expecting a non-null string. In Python, you can also pass 42, [], or the sqlalchemy module. None isn't special here. :) Also, to quibble a bit: Rust's std::ptr::null() is just a raw pointer, and isn't actually usable in safe Rust. Actual safe pointer types are guaranteed non-null and you'd use Option<&T> to specify that they're nullable. Raw pointers cannot be dereferenced in safe Rust. It is true that std::ptr::null() is in Rust's standard library, but Foreign.Ptr.nullPtr is in Haskell's, and the purpose is the same (a special raw-pointer type only used for FFI purposes), so Rust isn't any worse than Haskell here.
- mcguire 11y ago"In Python, you can also pass 42, [], or the sqlalchemy module. None isn't special here..." ...the interpreter's environment, my left elbow, the magic unicorn that lives under my steps,...
- kazinator 11y agoSorry, no. You can only pass a stable, property typed Python representation of your left elbow, or the magic unicorn, not the elbow or unicorn itself.
- Animats 11y agoC++ introduced non-null references. But they didn't enforce them. There are still "I'm so l33t I can use null references" people. The new move semantics use null references for things moved from. C++ is trying to do Rust-like borrow checking without a borrow checker. Errors result in de-referencing null and crashing. Rust doesn't have null, but it has Option<T>, which is often syntactic sugar for null. It's not that null is bad in itself. It's that having variables which might be null need to be distinguished from ones which can't be null.
- mcguire 11y ago"Rust doesn't have null, but it has Option<T>, which is often syntactic sugar for null." In a sense, yes. But you have to do something positive before you can pretend that nothing is something. And the call to unwrap can be annotated with an argument as for why it can never fail, or why it's unimportant if it panics, or even be outlawed by a style guide. "It's not that null is bad in itself. It's that having variables which might be null need to be distinguished from ones which can't be null." Amen, brother.
- nhaehnle 11y agoThe new move semantics use null references for things moved from. No. When you move the contents out of somewhere like an std::vector instance, that object is left in an undefined but valid state. You're free to continue working with that object (though the only meaningful thing you can usually do without undefined behaviour is destruction or the equivalent of clear). That's very different from null references.
- Animats 11y agoRight; you can't have an array of references, and it's "unique_ptr", not "unique_ref". But pointers do get set to null. If p1 is a unique_ptr, and you do p2 = std::move(p1); p1's pointer is set to null. Further uses of p1 will fail, or crash, or something. p1.get() returns the underlying pointer or null, apparently escaping the unique_ptr protection. It's far inferior to Rust's borrow checker.
- sampo 11y ago> If x is known to be a String, the type check succeeds; if x is known to be a Socket, the type check fails. So the author is trying to explain what the null pointer exception is, but uses the concept of sockets in his example. I wonder, how many people know what a socket is but don't know what null pointer exception is?
- njharman 11y ago> if (str == null || str == "") Why not have null (and emptystring) eval to false? And if not str: str = 'wordup'
- duaneb 11y agoI'm still not entirely convinced that NULL is a problem. But how NULL (and pointer types in general) work in C leads to much, much greater problems. Though, I think uninitialized variables or memory might be just as bad.
- tolmasky 11y agoOne of the problem's Maybe still has is in the deeper question of "why are you expecting None to be here?". Don't get me wrong, there are valid cases for this, and Maybe is certainly preferable to null across the board, but I think the movement to Maybe in the greater programming space will in many cases practically result in trading one set of explicit errors (crashes) for (a more subtle?) set of errors (behavioral). In particular, this style of programming will become frequent (from Swift documentation): if let roomCount = john.residence?.numberOfRooms { println("John's residence has \(roomCount) room(s).") } else { println("Unable to retrieve the number of rooms.") } Or from this blog post's own example: option.ifPresent(x -> System.out.println(x)); In other words, I think the core problem still hasn't been attacked and we may end up in the same situation we were in with exceptions originally: programmers will just throw up their hands and wrap everything in a Maybe/Optional and/or just maybe-protect until the compiler stops bugging them. At the end of the day, if you ? all your values then you end up with something equivalent to having everything be nullable and correctly null-checking them. Obj-C had a form of this with nil calling of methods silently doing nothing (so you end up with methods that "conveniently" don't happen, and don't crash! when the receiver is nil). However, despite having lots of legitimate uses (don't bother checking your delegate exists), it can still very silently sneak in to other parts of the code.
- fixermark 11y agoThis is a concern, but the advantage to Maybe is it makes this concern very explicit. SML/NJ code will not compile if you don't have a binding to handle the None for an option 'a type. The programmer does of course then have the option of doing None => raise Error but practically speaking, when deciding it's time to make your code more robust, it's a lot easier to text search for instances of "None => raise" than to search for the absence of proper handling of the possibility of a null arg or return value.
- paulddraper 11y agoExactly. You can still get errors due to things not being there. But your assumptions are explicitly stated. You see None => raise Error, and a little alarm goes off in your head, and you look around for a reassurance that we really want to do that. In contrast, you don't give x.toUpperCase() a second glance.
- xamuel 11y ago>NULL is a terrible design flaw, one that continues to cause constant, immeasurable pain Exaggeration much? Certainly not "the worst mistake of computer science". IPv4 is much worse, just for one example. NULL isn't even visible to end-users, many mistakes in CS are quite visible and really impact non-programmers' lives. NULL is just the color of the wallpaper in the engine room.
- _Ogre 11y agoIt is vastly more destructive than IP v4. This affects end users directly, every single day. The number of times applications have crashed due to NULL related errors is probably in the tens or maybe hundreds of billions. Each time is an interruption of people's work and in some cases it destroys hours of work.
- dragonwriter 11y ago> NULL isn't even visible to end-users In the same way HIV isn't visible; sure, the cause isn't visible to most people, but the adverse effects are.
- 15155 11y ago> IPv4 is much worse How is it worse, technically, beyond a constrained keyspace? IPv4 is an unfortunate, entrenched reality. NULL doesn't need to be, and isn't, in some ecosystems.
- contravariant 11y agoAs a mathematician I still think making "-1 % 7 == 6 % 7" evaluate to false is worse.
- paulddraper 11y agoSo you like Python.
- njharman 11y ago> 4. NULL makes poor APIs Then don't use it. The example given is a bad design. No phone number and not in cache should not return the same value. That bad design has nothing to do with nil, Return '' for no phonenumber.
- AndyKelley 11y agoWhen I look at my coworker's code, or an open source project's code, there is going to be usage of NULL. Your strategy is not pragmatic. The problem with language features is that people use them.
- jhallenworld 11y agoWell the article doesn't talk about performance, but at least for the double-maybe in the store example, there is one level of extra indirection in the machine code: return pointer to maybe, then return pointer from string from it. If you care about performance, you really need two predicate values to cover this case. You could have (void * )0 for no entry, and (void * )1 for entry exists, but is blank.
- paulddraper 11y agoThe title originally matched the blog post ("The worst mistake of computer science"), but then it was changed by a moderator. Maybe it could have been a less drastic change? "NULL, the worst mistake of computer science" or "The worst mistake of computer science: NULL"
- arielby 11y agoNUL-terminated strings aren't that bad: * unlike Pascal-style strings, they can be usefully sliced, especially if you can modify them strtok-style. * unlike (ptr,len) "Modern C buffers"/Rust-style strings, references to them are pointer-sized, and they can be used as a serialization format. This makes the kind of application that is based on cutting pieces of a string and passing them around a good measure faster, especially compared to say C++'s "atomically reference-counted, re-allocating at the slightest touch" std::string. This style of programming is not particularly popular nowadays, so buffer-strings are better-fitting. Its main problem is its multitude of edge-cases, which tend to demonstrate C's "every bug is exploitable" problem well.
- Strilanc 11y ago> unlike Pascal-style strings, they can be usefully sliced, especially if you can modify them strtok-style. Slicing Pascal-style strings is also easy and constant-time: just track the buffer, offset, and length of the slice of characters you want. Java used to do it implicitly whenever you called `substring`. > unlike (ptr,len) "Modern C buffers"/Rust-style strings, references to them are pointer-sized, and they can be used as a serialization format. Every C method that takes a character buffer either a) has a corresponding length parameter or b) is avoided because of the security risks. In practice this means that C also stores the length information, just on the side instead of combined into a struct with the buffer.
- arielby 11y ago> Slicing Pascal-style strings is also easy and constant-time: just track the buffer, offset, and length of the slice of characters you want. Java used to do it implicitly whenever you called `substring`. That's just coercing into a "modern C buffer" and slicing it. It has the disadvantage that coercion is not equality or subtyping - i.e. you will have to do lots of wrappings and unwrappings in mixed code. > Every C method that takes a character buffer either a) has a corresponding length parameter or b) is avoided because of the security risks. In practice this means that C also stores the length information, just on the side instead of combined into a struct with the buffer. You are surely talking about the buffer's capacity, not the string's length. These are distinct concepts. Anyway, functions that only read strings, and structs that only store them read-only, aren't interested in the capacity of any buffer. Anyway, C strings aren't responsible for the fixed-size buffers of Cold War-era code - that code uses fixed-size buffers for everything. Their main claim to fame is their popularity in parsing code, which is edge-case- and bug-prone.
- pmelendez 11y agoIn case the author is reading this thread: The entry for C++ in the final tables is: C++ | NULL | boost::optional, from Boost.Optional It does ignore that C++ has nullptr since C++11
- ffn 11y agolol the null / undefined issue in javascript is further exacerbated by the fact there is no int type and everything is just a "number".... yet the number 0 is still treated the same as null/undefined by js special forms like "if" and "==". This is particularly hilarious because it leads to some null-check bugs like: if ( user.getScrollPosition() ) { whatever(); } else { die(); } 99% of the time, the code would be fine, but if the user scrolls just right, the whole thing would die. Stuff like this is literally death to debug because this bug is effectively indeterministic and can't be reproduced.
- bcheung 11y agoWhy would you check the value instead of the presence of the value? Rookie mistake. user.getScrollPostion() != undefined or in CoffeeScript just use: user.getScrollPosition()? Arrays have various types of ints and floats of various word lengths and signs. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays https://developer.mozilla.org/en-US/docs/Web/JavaScript/Type...
- bcheung 11y agoHere is what is wrong with Option. option.ifPresent(x -> System.out.println(x)); So instead of just checking to see if it is NULL you want me to create an instance of a specialized class that holds my variable that has a method that acts like an "if" statement that I need to pass an anonymous function to that receives the value I already have? Why not just do: x && System.out.println(x) or: System.out.println(x) if x Or if you want to skip over the rest of the logic if it is null: return unless x System.out.println(x) I don't see why I need to introduce a new type system and complicate things. For default values you want me to create an instance of specialized class that holds my variable and has a method that allows me to get my value or return a default value? option.orElseGet(5) So instead of a memory lookup I now need the overhead of an entire function call? Why not just do: x ||= 5 Or better yet, just put it in your function declaration as a default value: myFunc = (x=5, y, z) -> ... The one benefit I see is type safety but it is extremely rare for experienced programmers working in dynamic languages to have bugs related to type. You are layering on abstractions and forcing programmers to go through hoops just to get at their data. It is more complicated and increasing the cognitive load. There's also the fact that you are going through functions and classes instead of just memory accesses. This makes code less performant as well.
- dragonwriter 11y ago> The one benefit I see is type safety but it is certainly not easier Type safety is one benefit, but composability is an important benefit (as, you know, is extensively discussed in the source article.)
- bcheung 11y agoComposability comes from functions, macros, and inheritance. What does that have to do with types? "In fact, composibility is really the fundamental issue behind many of these problems. For example, the Store API returning nil for non-existant values was not composable with storing nil for non-existant phone numbers." That's only because you invented some abstraction that got in your way in the first place. NULL is a perfectly acceptable value in dynamic languages and even in most document store databases.
- ursus_bonum 11y agoI was with the article until the part about null terminators on strings. Null terminators are nothing like a NULL reference. We could just as easily have dollar-sign-terminated strings and no one would be conflating that idea with the concept of NULL. Also, NULL pointers are the least problematic type of pointer if you ask me. If a pointer is NULL it is not likely a security problem, and certainly not on its own. Accidentally using a NULL pointer will cause a crash, but accidentally using any other pointer could cause unlimited damage.
- paulddraper 11y agoTo quote the article: > This is a bit different than the other examples, as there are no pointers or references. But the idea of a value that is not a value is still present, in the form of a char that is not a char. It has nothing to do with the similarity in name (NULL / NUL). As you said, it could be terminated with $. The similarity is that they are both sentinel values. NULL is a sentinel for types; NUL is a sentinel for char arrays. In both cases, they create exceptional and non-composable situations. Good explanation here: https://www.reddit.com/r/programming/comments/3j4pyd/the_worst_mistake_of_computer_science/cumbius https://www.reddit.com/r/programming/comments/3j4pyd/the_wor...
- ursus_bonum 11y agoI know the author claims that there's a meaningful similarity beyond the names. But that's where I disagree. I get that a NULL pointer is in some meaningful way not a pointer. Because it literally isn't pointing to anything. It is not the case that NUL is a char that is not a char. NUL is a perfectly good char. Some functions treat it specially, but other than that it's just a regular char. Even the sentinels similarity is quite weak. NULL is a sentinel that indicates "no valid object" whereas NUL is a sentinel that indicates the end of a perfectly valid object. Notice how we're not having this discussion about '\n' or the whitespace chars in general, even though they're also treated as sentinels by some functions.
- SNvD7vEJ 11y agoReading this article is like listening to a republican arguing against taxes.
- 51Cards 11y agoI've never had a problem with NULL as a "value". Null is the absence of content, a container that is truly empty, and that is frequently useful. As an example it allows you to differentiate between a numerical field that was never populated vs. one specifically set to Zero. This is often an important distinction.
- foota 11y agoI'm not a c++ expert, but doesn't char *myChar = 0; std::cout << *myChar << std::endl; // runtime error compile because it's undefined behavior?
- kevinchen 11y agothe point is that it's a straightforward-looking piece of code that has really strange behavior.
- derricki 11y agoThe best phrase from the article: "confusing sources of sloppy nullish behavior."