Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala ambiguity with paren-less function calls

Tags:

scala

Excuse the long set-up. This question relates to, but is not answered by, Scala: ambiguous reference to overloaded definition - best disambiguation? .

I'm pretty new to Scala, and one thing that's throwing me off is that Scala both:

  • Has first-class functions
  • Calls functions when using object-dot notation without any parenthetical argument lists (as if the function were a property)

These two language features are confusing me. Look at the below code:

class MyClass {
    def something(in: String): String = {
        in + "_X"
    }

    def something: String => String = {
        case _ => "Fixed"
    }
}

val my = new MyClass()

println(List("foo", "bar").map(my.something))

I would expect this to print List("foo_X", "bar_X") by calling the something prototype that matches the map's required String => ? argument. Instead, the output is List("Fixed", "Fixed") - Scala 2.11 is invoking the no-argument something() and then passing its return value to the map.

If we comment out the second no-argument prototype of something, the output changes to be the expected result, demonstrating that the other prototype is valid in context.

Adding an empty argument list to the second prototype (making it def something()) also changes the behavior.

Changing the my.something to my.something(_) wakes Scala up to the ambiguity it was silently ignoring before:

error: ambiguous reference to overloaded definition,
both method something in class MyClass of type => String => String
and  method something in class MyClass of type (in: String)String
match argument types (String)
println(List("foo", "bar").map(my.something(_)))

Even using the supposedly-for-this-purpose magic trailing underscore doesn't work:

val myFun: (String) => String = my.something _

This results in:

error: type mismatch;
 found   : () => String => String
 required: String => String
val myFun: (String) => String = my.something _

My questions:

  • If I have MyClass exactly as written (no changes to the prototypes, especially not adding an empty parameter list to one of the prototypes), how do I tell Scala, unambiguously, that I want the first one-argument version of something to pass as an argument to another call?
  • Since there are clearly two satisfying arguments that could be passed to map, why did the Scala compiler not report the ambiguity as an error?
  • Is there a way to disable Scala's behavior of (sometimes, not always) treating foo.bar as equivalent to foo.bar()?
like image 948
Borealid Avatar asked Jul 12 '15 19:07

Borealid


1 Answers

I have filed a bug on the Scala issue tracker and the consensus seems to be that this behaviour is a bug. The compiler should have thrown an error about the ambiguous reference to "my.something".

like image 162
Borealid Avatar answered Nov 19 '22 01:11

Borealid