6
votes

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()?
1
This is discussed in Atomic Scala in the "A Bit of Style" chapter. If a method has no arguments it can be defined with parentheses or not. If it is defined with parentheses it can be called with or without them. However if it is defined without parentheses it can only be called without them. A stylistic convention is to define methods with parentheses if calling them changes the internal state of the object and otherwise to define them without parentheses . In your contrived example it seems the compiler did its best to pick the right function based on signatures including parentheses.user4322779

1 Answers

2
votes

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".