traps for the unwary: today's java/arrays/variance/inference/overload issue

51 views
Skip to first unread message

Paul Phillips

unread,
May 2, 2012, 11:06:43 AM5/2/12
to scala-l...@googlegroups.com
This was sufficiently non-obvious I thought it was of general
interest. You can run into this different ways, and without involving
Arrays or java, such as:

scala> def f(x: Mutable) = 1 ; def f(x:
collection.mutable.Set[AnyRef]) = 2 ; f(collection.mutable.Set("abc"))
f: (x: Mutable)Int <and> (x: scala.collection.mutable.Set[AnyRef])Int
f: (x: Mutable)Int <and> (x: scala.collection.mutable.Set[AnyRef])Int
res0: Int = 1

Choosing the "most specific type" while resolving the overload can
lead to choosing the "least specific type" from the argument types
among the overload choices, because (as in the above) choosing
mutable.Set[String] excludes the second choice, so it never gets to
the point where it could infer mutable.Set[AnyRef].

Also of note here is that java and scala -- calling the same
overloaded method with the same typed arguments -- make different
choices, because Array[String] matches Array[Object] in java but not
in scala. Unfortunately people write java classes, even in the jdk
itself (e.g. javax.swing.tree.TreePath, see below) generally thinking
only of java overloading semantics, so they do crazy things like
overloading Object and Object[] and expecting good things to come of
it.

::: Resolution from https://issues.scala-lang.org/browse/SI-5719

This is damage inflicted by a combination of lossy java interop and
the insanity of constructors which take "Object", which isn't so hot
in any language.

Notice that TreePath has two public constructors:

TreePath(Object lastPathComponent)
TreePath(Object[] path)

So what happens if you say

new TreePath(Array("abc"))

Overloading resolution has to choose a constructor. An Array[String]
is not an Array[Object] (arrays are not covariant, not in this
language) so it chooses the Object constructor, which of course an
Array is.

Were there no Object constructor, the expression would not typecheck
as Array[String] and the inferencer would choose Array[Object]. But as
it is, it's just following orders.

You can work around this by specifying the type.

new TreePath(Array[Object]("abc"))

or whatever the equivalent is in your code.

scala> val x1 = new TreePath(Array("abc"))
x1: javax.swing.tree.TreePath = [[Ljava.lang.String;@77c16c5f]

scala> val x2 = new TreePath(Array[Object]("abc"))
x2: javax.swing.tree.TreePath = [abc]
Reply all
Reply to author
Forward
0 new messages