Hello,
Thanks a lot for your efforts. This is a really hard problem, and I’m glad it’s been tackled to that depth. When I first saw the idea of the T | JavaNull
, I said it was the first design I’d seen that had a chance not to crash and burn; the last time I said something like that, @odersky replied: “[Coming from you], I take that as strong endorsement.”
Obviously, I have quite a number of comments
Because of the unsoundness, we need to allow comparisons of the form x == null
or x != null
even when x
has a non-nullable reference type (but not a value type). This is so we have an “escape hatch” for when we know x
is nullable even when the type says it shouldn’t be.
I disagree with that reasoning, for two reasons:
- a normal program never tests whether a
val x = something
is in fact null
to test around initialization problems. This is a false problem, it shouldn’t be tackled.
- there is already an escape hatch if someone really really wants to do that:
(x: T | Null) == null
That said, you can’t prevent anyone from ever doing x == null
regardless of the type of x
, because ==
is defined on Any
, so this whole thing is quite moot anyway. It’s possible to warn when trying to compare a non-nullable type with null
, though, like scalac already does for primitive types:
Welcome to Scala 2.12.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_60).
Type in expressions for evaluation. Or try :help.
scala> val x: Int = 5
x: Int = 5
scala> x == null
<console>:13: warning: comparing values of types Int and Null using `==' will always yield false
x == null
^
res0: Boolean = false
Similarly, I think the section on Reference equality is biased by the same above false issue. Here it’s even more annoying because you have to add an entirely new top-type, that is magical, in the type system. I strongly suggest to remove all of that. I can use x == null
instead of x eq null
anyway, if I want to do that! And yes, == null
is always as efficient as eq null
, so performance is not a good excuse either.
x.nn
, as a method name, does not follow established conventions for methods in the Scala library: alphanumeric method names should be full words, not initials (eq
and ne
are unfortunate precedents, but let’s not exacerbate the issue). This is one of the cases where I would recommend a symbolic operator, such as x.!!
. Otherwise, spell out x.ensureNotNull
or something like that.
In addition, for both the above method, and the implicit conversions that you mention below, I strongly suggest that they be hidden behind an import, such as
import scala.NullInterop._
I could live with x.!!
always in scope, but please don’t put unsound implicit conversions in scope for every program, every time.
Nullification function (ref in doc)
I am confused why nf(A | B)
and nf(A & B)
need to be defined. IIRC, nf
is only applied to types loaded from Java source files and Java-generated class files, and those would never contain |
or &
types. Can you elaborate on an example where this is relevant?
I agree with @smarter’s comment on the PR that it is problematic that we cannot write down a JavaNull
type in the source code. I fail to see any advantage to that restriction. Can we simply lift it?
Flow-sensitive inference (ref in doc)
Although I sympathize with the issue that this feature addresses, I can’t help but have a bad feeling about it. There is no precedent in Scala for anything like that, and I am wary of adding it as part of such an important change. I believe pattern matching should be enough to safely deconstruct nullable types.
Besides, if we really want this, I find it very odd that it only supports null
. I would expect it to support other idioms, such as
if (x.isInstanceOf[Foo]) {
val y: Foo = x // typechecks because I know `x` is a `Foo`
}
At the very least, I believe that this flow-sensitive inference should be presented as an orthogonal proposal. Fundamentally, it has nothing to do with non-nullable types.
That’s all for today Thanks again for all your hard work. I hope we can get this all the way to the finish line.