Ambiguous overload even one is more specific

162 Views Asked by At

I have two definition of foo, and one of them is more supposedly specific

def foo(f: java.util.function.ToIntFunction[String]) = println("foo1")
def foo[T](f: String=>T) = println("foo2")
//def foo[T](f: String=>T)(implicit d: DummyImplicit) = println("foo2")    //Does not work either


foo({_: String => 1})            //Should pick foo1, but gives error instead

Error is:

error: ambiguous reference to overloaded definition,
both method foo in object Main of type [T](f: String => T)Unit
and  method foo in object Main of type (f: java.util.function.ToIntFunction[String])Unit

I also tried the trick of DummyImplicit but it still gives the same error. How can I achieve compile time overload when Int presents without using reflection?

I am using Scala 2.12 with SAM type support.


Edit

I hope to get a solution that is not limited to using Java converters, because ToIntFunction interface can be replaced by Scala trait, e.g.

trait ToIntFunction[T] { def apply(v: T): Int }
def foo(f: ToIntFunction[String]) = println("foo1")
def foo[T](f: String=>T) = println("foo2")

as I think it is a more generic problem for method overload.

2

There are 2 best solutions below

1
Mario Galic On

On my machine with Scala 2.12 foo({_: String => 1}) evaluates to foo2, thus I am unable to reproduce the issue. My interpretation of SAM conversion in overloading resolution in Scala 2.12 is that Function-typed arguments take precedence, thus it should evaluate to foo2:

In order to improve source compatibility, overloading resolution has been adapted to prefer methods with Function-typed arguments over methods with parameters of SAM types.

Note _: String => 1 is Function1. To force it to evaluate to foo1 try asJava from scala-java8-compat like so:

import scala.compat.java8.FunctionConverters._
foo({_: String => 1}.asJava) // foo1

As per Krzysztof Atłasik's comment it is reproducible in Scala 2.13.

0
SwiftMango On

I found a solution using macro expansion:

object Foo {
    import scala.reflect.macros.Context
    import scala.language.experimental.macros

    def foo[T,R](f: T=>R) = macro fooImpl[T,R]

    def fooImpl[T: c.WeakTypeTag, R: c.WeakTypeTag](c: Context)
        (f: c.Expr[T=>R]): c.Expr[Unit] = {
        import c.universe._
        if(c.weakTypeOf[R] == c.weakTypeOf[Int]) {
            reify { println("Int") }
        }
        else {
            reify { println("Not Int") }
        }
    }
}

In another file,

object Test extends MainApp {
  Foo.foo {_:String => 0}    //expands into a simple println and it prints "Int"
  Foo.foo {_:String => 0d}   //prints "Not Int"
}

This is definitely not ideal, but solves the problem. Hope someone can give an answer without using macro.