Match type question wrt Null in 3.4

When using explicit nulls, following the SIP I’m trying to rely solely on subtype relationship, and so I tried the following match type

type AdmitsNull[T] = Null match {
  case T => true
  case _ => false
}

summon[AdmitsNull[String | Null] =:= true] // works

summon[AdmitsNull[String] =:= false]
//Cannot prove that AdmitsNull[String] =:= (false : Boolean).
//Note: a match type could not be fully reduced:
//  trying to reduce  AdmitsNull[String]
//  failed since selector Null
//  is uninhabited (there are no values of that type)

I don’t quite understand the thing about null being uninhabited (although it seems to me that it is, after all null is a value of that set, unlike the Nothing type), since the match is not asking that question, but rather if T is a super type of Null.

If I change Null to Unit, it works in every case too and Unit should be the same (as I understand it) as Null in terms of inhabitants.

1 Like

Unit has only one inhabitant, namely (), to my knowledge.
I knew about Nothing having no inhabitant, but didn’t know about Null.

There was a typo, I meant that I don’t understand the error about Null being not inhabited.

2 Likes

@rcano Are you using both -Yexplicit-nulls and -language:unsafeNulls by any chance?

Your code works for me on Scala 3.4.0 with only -Yexplicit-nulls.

Only -Yexplicit-nulls, the other is not in the scalac options nor imported in the file.

Oh, sorry, I thought I was using 3.4.0 in my testing but I was actually using 3.3.3.

It works with Scala 3.3.3 but not 3.4.0 :-/

To be clear, this compiles:

//> using scala 3.3.3
//> using options -Yexplicit-nulls

type AdmitsNull[T] = Null match {
  case T => true
  case _ => false
}

def testAdmitsNull(): Unit =
  summon[AdmitsNull[String | Null] =:= true]
  summon[AdmitsNull[String] =:= false]

but changing to using scala 3.4.0 makes the same code fail with:

Compiling project (Scala 3.4.0, JVM (21))
[error] ./AdmitsNull.scala:11:39
[error] Cannot prove that AdmitsNull[String] =:= (false : Boolean).
[error] 
[error] Note: a match type could not be fully reduced:
[error] 
[error]   trying to reduce  AdmitsNull[String]
[error]   failed since selector Null
[error]   is uninhabited (there are no values of that type).
[error]   summon[AdmitsNull[String] =:= false]
[error]                                       ^
Error compiling project (Scala 3.4.0, JVM (21))

… as you had initially reported. Sorry for the noise.

Null does have exactly 1 inhabitant, namely null.

However, for match types, we consider Null to be a bottom type like Nothing. This is a compromise. If we did not do this, nothing would ever be disjoint from anything because they would always have null in common.

With -Xexplicit-nulls we might change that, but it has to be part of what the explicit nulls proposal brings to the table.

2 Likes

Ah, thanks, that clarifies it.