user4601931
user4601931

Reputation: 5285

Testing an array for emptiness in Scala

The question isn't about how to test an array for emptiness (arr.length == 0 does this fine). Rather my question is, why does

scala> Array().isEmpty
res1: Boolean = true

work and

scala> val x = Array[String]()
x: Array[String] = Array()
scala> x.isEmpty
res2: Boolean = true

work, but

scala> val y = Array()
y: Array[Nothing] = Array()

scala> y.isEmpty
<console>:13: error: value isEmpty is not a member of Array[Nothing]
       y.isEmpty
         ^

does not?

Upvotes: 5

Views: 8864

Answers (2)

Maxim
Maxim

Reputation: 7348

EDIT: probably this answer is not correct. But I'm keepeing it here to show how did I try to investigate this issue. To me it looks like a bug in the compiler.

The answer is implicit conversion that exists for Array[T <: AnyRef] String is AnyRef, Nothing is not AnyRef.

How could you discover this yourself?

In IntelliJ you can see a grey underline under the isEmpty enter image description here

This means that the method isEmpty is not a method of Array, but an implicit (a method on a class that has implicit conversion from Array).

Now, to discover the implicit conversion, just click ctrl+q enter image description here

So when you follow it, you get to this line in the source code -

  implicit def refArrayOps[T <: AnyRef](xs: Array[T]): ArrayOps[T]    = new ArrayOps.ofRef[T](xs)

That explains that the implicit conversion is on an Array[T] when T extends AnyRef

So back to our case - String <: AnyRef, but this doesn't hold for Nothing

Upvotes: 3

badcook
badcook

Reputation: 3739

As @MichaelZajac points out, Nothing is a subtype of everything (its counterpart Any is a supertype of everything). In particular it's also a subtype of AnyRef. In fact there's an even more general genericArrayOps which has no type bound at all (e.g. Array[Any]().isEmpty works)! The implicit conversion allowing you to use isEmpty should kick in, but of course it doesn't, even though explicitly calling the conversion is fine.

The link @slouc gives is the answer, namely that the Scala compiler treats Nothing in a special way when doing implicit resolution because Nothing is the default lower bound for a type when doing type inference.

Now why exactly would it ever be desirable for Nothing not to be considered in implicit resolution? Well the tricky thing about Nothing again is that it's a subtype of everything. This means that if at any point Scala infers a type to be Nothing, every single implicit conversion would immediately become valid. This could hide a type error (you should never have an instance of Nothing, but when that Nothing becomes an Int... well who's to say?). (Note I would love for someone who actually hacks on the compiler to jump in and confirm/deny/elaborate on this)

Upvotes: 4

Related Questions