Make Null a subclass of AnyVal under -Yexplicit-nulls

To me it seems like there is broad consensus that “null should only live at the interop boundary”

Why I think there is consensus

For any relevant task, Scala was designed in a way that both makes it hard to use null and makes it easy to use something else.

For example if you want “a value of T or not”:
Using Option[T], you get a lot of tools to do things (deconstructing, map, orElse, …)
And using T | null, you essentially get nothing, you have nn and if, and on top of that toString blows up in your face !
To contrast, Kotling makes it easy to use null, you get x?.foo, x ?? someDefault, x!!, x.toString works, and even the type is made short: T?

So naturally we don’t want to touch null if we don’t have to !


I think the real disagreement here is “How can we ensure we don’t have to worry about null ?”

And there seem to be two camps: “Keep null being weird, but shove it as far away as possible, and give me a stick to keep it away” and “Let’s make null normal and boring so we can manipulate it like anything else”
(“the stick” is AnyVal | AnyRef/NonNull, “making it boring” is ensuring toString/etc don’t blow up in our faces, and stopping it being a special case in the type hierarchy and typer)

And I would expect that everyone also agrees that:

  1. if the value null behaved in the same boring way that () does, it would be good (Proposal: Fixing null.methodOfAny() under explicit-nulls)
  2. if we degrade interop, it would be bad ("MINUS_NULL" and the future of explicit nulls)

And as a result, I think the way to resolve this issue is to prove that we can do the first without doing the second.

It even seems like there is a path we can follow to gradually shed doubts:

  1. Do Proposal: Fixing null.methodOfAny() under explicit-nulls (without Null <: AnyVal): null is less of a footgun (not a footgun anymore ?) and we preserve the ability to exclude it from generics => no possible loss wrt interop
  2. Make Null <: AnyVal and add NonNull, makes null even more normal, and we preserve same capability wrt interop
  3. Remove NonNull once we show that it is not useful anymore, either because it never was, or because we added feature(s) tailored to better suiting what it was useful for

They make sense as steps, but note that 2 and 3 would most likely have to happen in the same release, otherwise we would have too many backward compat issues.

1 Like