How can I differentiate between def foo[A](xs: A*) and def foo[A, B](xs: (A, B)*)?
overloading, scala, type-erasure
Solution
You can, in a fairly round about way. `Foo` is a type class, and the compiler implcitly passes an instance of the type class, compatible with the (inferred) type parameter `A`.
trait Foo[X] {
def apply(xs: Seq[X]): Unit
}
object Foo {
implicit def FooAny[A]: Foo[A] = new Foo[A] {
def apply(xs: Seq[A]) = println("apply(xs: Seq[A])")
}
implicit def FooTuple2[A, B]: Foo[(A, B)] = new Foo[(A, B)] {
def apply(xs: Seq[(A, B)]) = println("apply(xs: Seq[(A, B)])")
}
def apply[A](xs: A*)(implicit f: Foo[A]) = f(xs)
}
Foo(1, 2, 3) // apply(xs: Seq[A])
Foo(1 -> 2, 2 -> 3) // apply(xs: Seq[(A, B)])
In the second call, both `FooAny` and `FooTuple2` could be passed, but the compiler picks `FooTuple2`, based on the rules of static method overloading. `FooTuple2` is considered more specific that `FooAny`. If two candidates are considered to be as specific as each other, an ambiguity error is raised. You can also prefer one over the other by placing one in a superclass, as is done in `scala.LowPriorityImplicits`.
UPDATE
Riffing off the DummyImplicit idea, and the followup thread on scala-user:
trait __[+_]
object __ {
implicit object __ extends __[Any]
}
object overload {
def foo(a: Seq[Boolean]) = 0
def foo[_: __](a: Seq[Int]) = 1
def foo[_: __ : __](a: Seq[String]) = 2
}
import overload._
foo(Seq(true))
foo(Seq(1))
foo(Seq("s"))
This declares a type-parameterized trait `__`, covariant in its unnamed type parameter `_`. Its companion object `__` contains an implicit instance of `__[Any]`, which we'll need later on. The second and third overloads of `foo` include a dummy type parameters, again unnamed. This will be inferred as `Any`. This type parameter has one or more context bounds, which are desugared into additional implicit parameters, for example:
def foo[A](a: Seq[Int])(implicit ev$1: __[A]) = 1
The multiple parameter lists are concatenated into a single parameter list in the bytecode, so the double definition problem is circumvented.
Please consider this as a opportunity to learn about erasure, context bounds and implicit search, rather than as a pattern to be applied in real code!
Problem
I know that type erasure makes them look equal, type-wise, at runtime, so that: ``` class Bar { def foo[A](xs: A*) { xs.foreach(println) } def foo[A, B](xs: (A, B)*) { xs.foreach(x => println(x._1 + " - " + x._2)) } } ``` gives the following compiler error: ``` <console>:7: error: double definition: method foo:[A,B](xs: (A, B)*)Unit and method foo:[A](xs: A*)Unit at line 6 have same type after erasure: (xs: Seq)Unit def foo[A,B](xs: (A, B)*) { xs.foreach(x => println(x._1 + " - " + x._2) ) } ^ ``` But is there a simple way to be able to write: ``` bar.foo(1, 2, 3) bar.foo(1 -> 2, 3 -> 4) ``` and having these call different overloaded versions of foo, without having to explicitly name them: ``` bar.fooInts(1, 2, 3) bar.fooPairs(1 -> 2, 3 -> 4) ```