Tried with dotr in Dotty compiler version 0.17.0-RC1 .
def isAType(arg: String): Unit = arg match {
case _ : "a" => println("This is `a` type")
case _ => println("not `a`")
}
isAType("a")
//This is `a` type
isAType(new String("a"))
//This is `a` type
new String("a").isInstanceOf["a"]
//val res0: Boolean = false
new String("a") should not match with case _ : "a" as it doesn't with isInstanceOf.
The fact that pattern matching and isInstanceOf don't agree is definitely a bug, but (new Integer(1)).isInstanceOf[1] returns true in both scalac and dotty (because it erases to 1.equals(new Integer(1))), so I would argue that x.isInstanceOf["a"] should similarly erase to "a".equals(x). In fact, SIP-23 already says:
Pattern matching against typed patterns (see 8.1.2 Typed Patterns) where the TypePat is a literal type is translated as a match against the subsuming non-singleton type followed by an equality test with the value corresponding to the literal type.
/cc @milessabin @sjrd, do you agree ?
Yes, I believe that x.isInstanceOf["a"] should compile down to "a".equals(x).
Hm, now that you mention it, I think we did talk about it already w/ @sjrd. My sole argument as to why this should not be the case is that all AnyRef singleton types work based on object identity, and making String work based on equality instead is inconsistent.
Something to consider - how should the following compile?
val x: "a" = "a"
val y: Object = x
foo.isInstanceOf[x.type]
foo.isInstanceOf[y.type]
That is a concern, indeed. But it is not limited to strings. You can also construct something with Ints:
val x: 1 = 1
val y: Integer = x
foo.isInstanceOf[x.type]
foo.isInstanceOf[y.type]
Ok, I just checked and apparently there's ample precedent for interesting behaviour when upcasting/boxing AnyVals:
scala> val a: Any = 1
val a: Any = 1
scala> 1.isInstanceOf[a.type]
val res0: Boolean = true
scala> a.isInstanceOf[1]
val res1: Boolean = true
scala> (new Integer(1)).isInstanceOf[a.type]
val res2: Boolean = true
scala> val i: Integer = new Integer(1)
val i: Integer = 1
scala> val j: Integer = new Integer(1)
val j: Integer = 1
scala> i.isInstanceOf[a.type]
val res3: Boolean = true
scala> j.isInstanceOf[a.type]
val res4: Boolean = true
scala> i.isInstanceOf[j.type]
val res5: Boolean = false
scala> i eq j
val res6: Boolean = false
scala> i.isInstanceOf[1]
val res7: Boolean = true
scala> 1.isInstanceOf[i.type]
val res8: Boolean = false
scala> val o: Object = i
val o: Object = 1
scala> o.isInstanceOf[a.type]
val res9: Boolean = true
scala> a.isInstanceOf[o.type]
val res10: Boolean = false
Note in particular that upcasting an Int to Any preserves isInstanceOf behaviour, but boxing it in Integer/Object does not.
Note in particular that upcasting an Int to Any preserves isInstanceOf behaviour, but boxing it in Integer/Object does not.
upcasting ends up calling Integer.valueOf which caches small integers (< 1024), try a big one.
My sole argument as to why this should not be the case is that all AnyRef singleton types work based on object identity, and making String work based on equality instead is inconsistent.
Maybe we need to reconsider the concept of literal _singleton_ types, if we stop insisting that they're singletons, then I think that using == is completely natural.
Seems this needs more deliberations.
Most helpful comment
Yes, I believe that
x.isInstanceOf["a"]should compile down to"a".equals(x).