Open scabug opened 9 years ago
Imported From: https://issues.scala-lang.org/browse/SI-9395?orig=1 Reporter: Bryan Jacobs (bryanjacobs) Affected Versions: 2.11.4 See #8342, #8344, #8800
Ian Bellamy (imbellish) said (edited by @lrytz on Jul 15, 2015 6:16:55 AM UTC): Possible debug script can be found here:
scala> class MyClass {
| def something(in: String): String = {
| in + "_z"
| }
| def something: (String) => String = {
| case _ => "Fixed"
| }
| }
scala> val my = new MyClass()
my: MyClass = MyClass@155cf22
scala> println(List("x", "y").map(my.something))
List(Fixed, Fixed)
scala> class MyClass {
| def something(in: String): String = {
| in + "_z"
| }
| def somethingelse: (String) => String = {
| case _ => "Fixed"
| }
| }
defined class MyClass
scala> println(List("x", "y").map(my.something))
List(Fixed, Fixed)
scala> val my = new MyClass()
my: MyClass = MyClass@15a4a5
scala> println(List("x", "y").map(my.something))
List(x_z, y_z)
scala> println(List("x", "y").map(my.somethingelse))
List(Fixed, Fixed)
@lrytz said (edited on Jul 15, 2015 6:49:52 AM UTC): In my reading of the spec, this should be ambiguous. Shorter example:
object Test extends App {
def f(s: String): String = "1"
val f: (String) => String = s => "2"
val t: String => String = f
println(t("")) // 2
}
http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#overloading-resolution
The reference to f
is not an application or a type application (last paragraph of overloading resolution). Both definitions of f
are compatible to the expected type, the method via eta-expansion (http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#implicit-conversions), so the most specific version is chosen:
If I'm wrong in reading the spec, it would be good to add an example to it explaining this case.
Dotty says 1
.
[[syntax trees at end of typer]] // t9395.scala
package <empty> {
final lazy module val Test: Test = new Test()
final module class Test() extends Object(), App { this: Test.type =>
def f(s: String): String = "1"
val f: String => String =
{
def $anonfun(s: String): String = "2"
closure($anonfun)
}
val t: String => String =
{
def $anonfun(s: String): String = Test.f(s)
closure($anonfun)
}
println(Test.t.apply(""))
}
}
Is this like the Monty Hall problem where you always pick the other door?
This code:
Prints List(Fixed, Fixed).
The type checker is calling the no-argument variant of my.something(), and then silently taking the return type and passing it to map(). This ignores the other matching one-argument version of my.something.
The below line (arguably correctly) produces a compile error about an "ambiguous reference to overloaded definition":
Adding parentheses to the no-argument definition of the method removes the issue. Changing the return type of the no-argument method to be anything other than String => String removes the issue.
I think this is incorrect behavior; where there is a conflict between the result of an implicit conversion (the my.something() return value) and an unconverted correct type, either the unconverted type should win or a compile error should be thrown telling the user about ambiguity. In other words, the above code should either output List(x_z, y_z) or fail to compile. Currently the implicitly converted version is silently taking precedence.