Home > Software design >  Ambiguous Overload in Scala 3 with Varargs
Ambiguous Overload in Scala 3 with Varargs

Time:06-03

I have the following code.

object example {

  def foo(a: Any) = "Object"

  def foo(a: String, args: String*) = "String"

  def main() = {
    println(foo("ABC")) // Should print "String"
  }

}

In Scala 2, this code works fine. When I call foo with a single string argument, both overloads are valid but the String one is more specific, so it gets called. In Scala 3, this is ambiguous.

-- [E051] Reference Error: overloads.scala:9:12 --------------------------------------------------------------------
9 |    println(foo("ABC"))
  |            ^^^
  |            Ambiguous overload. The overloaded alternatives of method foo in object example with types
  |             (a: String, args: String*): String
  |             (a: Any): String
  |            both match arguments (("ABC" : String))

The equivalent code (two overloads, one of them more specific and having a vararg parameter) also works in Java. What exactly changed in Scala 3 to make this particular call ambiguous? Is this behavior intended? And how can I tell Scala which overload to call in this case?


For what it's worth, I am well aware method overloading is strongly discouraged in Scala. In my actual code, I'm interfacing with a Java builder class that has an overloaded .append method, so I have no control over the function signatures.

CodePudding user response:

This behaviour is mentioned in this PR, in particular in this comment. I don't know if that will be "fixed" or if it's exactly as intended.

Note that the following is not ambiguous:

def foo(s: String)
def foo(a: String, other: String*)
foo("fine!")

With def foo(s: Any), you can explicitly select one overload or the other like this:

foo("fine": Any) // calls foo(Any)
foo("fine", Nil: _*) // calls foo(String, String*) with an empty vararg

PS: I wouldn't say that overloading is "strongly discouraged", but we must be careful and admit that some cases are ambiguous, to the compiler and to humans. It's not obvious to me which definition foo("") should refer to, so it's not completely crazy to be more explicit in that case.

  • Related