Live data from Hacker News

NULL: The worst mistake of computer science? (2015)

lucidchart.com

241–250 of 377 posts

Re: NULL: The worst mistake of computer science? (2015)

#241
post #226

It 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…

> 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.

Re: NULL: The worst mistake of computer science? (2015)

#242
post #50

> 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 NU…

I largely agree, but that would get very tedious because there's no way to create a pointer type that is guaranteed to be non-null in C. You'd end up having to do a lot of unnecessary checks because of that.

People say optionals are the solution, but the way I see it it's the other way around. Pointers types that allow NULL are basically optionals, and the problem is that we use them everywhere, even for things that are not optional. What we are missing are pointer types for things that are not optional.

And the pointer type with a guaranteed value needs to be as easy to use as the nullable pointer type, if not easier. Otherwise it won't always be used when it should be.

Re: NULL: The worst mistake of computer science? (2015)

#243
post #23

This 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.…

I totally disagree - NULL gives much worse ergonomics all-around. While modifying code, I'm constantly afraid of whether the value I'm accessing could be NULL. Most SQL schemas are filled with "NOT NULL" to the point of ridiculousness, and most Java methods that I've seen tend to have @NotNull used everywhere too. Not having NULL gives you a lot of confidence when reading and writing code, by guaranteeing that your object does indeed exist.

Re: NULL: The worst mistake of computer science? (2015)

#244

The 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.

TypeScript 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/t... [1]: https://kotlinlang.org/docs/reference/null-safety.html

Re: NULL: The worst mistake of computer science? (2015)

#245
Thanks 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.

Re: NULL: The worst mistake of computer science? (2015)

#246
post #223
post #205

Earlier quoted context omitted.

Yes, option types are awesome. No, they are not nulls. Algebraic data types are not direct support "no data found at the type level". Algebraic types are really just a fancier enum/union type. It just so happens that inventing special sentinel values is awesome when you have an algebraic type system to check your work.

Take a look at Kotlin or Typescript†. Basically, they decided to fully design the language with support for null-as-option. That means several things: * T (non-nullable) and T? (nullable) are different types. T? = T | null * Where T is expected T? is not accepted, but where T? is specified T is also accepted * T? is automatically cast to T in the places where it's asserted to be not null, e.g. within an if(x != null)…

These are great features, but they are really just syntactic sugar over algebraic types. It's not a more pleasant developer experience than sum (Option) types, it's a more pleasant experience with sum types. Ex:

    macro! unwrap(x, fallback) {
      match x {
        Some(n) => n,
        None => fallback
      };
    }
> Typescript goes even further and has the best enumeration support I've seen any language have. T | U is a fully valid type, and if T | U is asserted to be one of them it is automatically cast to T/U. It is a very natural and efficient way of building ADTs

That's pretty cool. It seems like refinement types. [1]

[1]: https://en.wikipedia.org/wiki/Refinement_(computing)#Refinem...

Re: NULL: The worst mistake of computer science? (2015)

#247
post #9

I 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.

Zero's behavior is totally consistent with the other numbers, though - it doesn't break associativity, commutativity, or any of the other stuff you'd expect. On the other hand, NULL takes every type I've ever written and adds an instance whose behavior with every function is, at best, to crash my program, and at worst, completely undefined. Its behavior is not at all consistent with the other instances.

Re: NULL: The worst mistake of computer science? (2015)

#248
post #161
post #79

Earlier quoted context omitted.

This is not even true in C. 0 is a "null pointer literal" when used in pointer context, this does not imply that the actual null pointer has a value of zero.

Ah yes you are right, an interesting point however, 6.3.2.3 Pointers 3. An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function. 4. Conversion of a null pointer to another…

Saying “equal to any 0-valued pointer” there can be misleading. It is true of a pointer assigned from a (foo*)0 constant, but not true of a pointer to a hardware address 0 or a pointer with bits all 0 (assuming they exist).

Re: NULL: The worst mistake of computer science? (2015)

#249
post #164

Earlier quoted context omitted.

I 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.

If you know something can't be null, then don't use an option. Simple as that. For example, a SQL library can return a non-nullable column of String as just a String, not an Option[String]. Thus, you actually get a solid distinction that you don't get with null pointers. There's no reason to include sentinels that will randomly blow up your program.

No. The point is that the data structure can't know if there's a NULL since the data structure is static. Context is dynamic. Code is dynamic as well, and it can know that some things must exist based on other dynamic conditions.

So this "solid" distinction often is just noise and actually blurs the intention of the programmer: An explicit unwrap is required syntactically while it should not be required semantically because really the option data is not an option but a requirement in certain contexts.

Re: NULL: The worst mistake of computer science? (2015)

#250
post #201

Earlier quoted context omitted.

It's the worst mistake because it made you believe that its atrocious ergonomics are actually superior to more sensible solutions. Implicit nullability doesn't really save you any null checks. It just makes it possible to forget necessary checks. It was fine to design a language with nullable pointers in the 70s. It's unacceptable nowadays. nil in Go is a major mistake.

Okay. So let's say we get rid of nil in Go. Now, structs with pointers have no zero value. Slices and maps have no zero value. Funcs have no zero value. Reflect can no longer create objects because it can't possibly enforce that you initialize the pointers. Functions that return either an error or a value now need a new pattern, probably requiring generics or another special type. Map access needs to return this spec…

> Now, structs with pointers have no zero value.

A zero value is much better than undefined value, I'll grant you that. I prefer the forced initialization approach (Haskell, presumably Rust and many others). If I add a new field, I want to know where I need to populate it. Or if you must, maybe a default value defined on the struct (perhaps that's also "considered harmful" for reasons I can't think of at the moment).

But it seems you prefer the ergonomics of default-zero. I don't get it, but I can't argue with preference.

Post reply on HN