15

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)
Aaron Novstrup
  • 20,477
  • 7
  • 67
  • 107
Knut Arne Vedaa
  • 14,304
  • 11
  • 46
  • 58
  • The most straight-forward way to do this is with a context bound ClassManifest for each of the type parameters: `def foo[A : ClassManifest](xs: A*) ...`. I added this as an answer with more commentary below. – Aaron Novstrup Aug 12 '10 at 18:14
  • When overloading consider: http://stackoverflow.com/questions/2510108/why-avoid-method-overloading – Aaron Novstrup Aug 17 '10 at 14:59
  • See also: http://stackoverflow.com/questions/3307427/scala-double-definition-2-methods-have-the-same-type-erasure – retronym Aug 17 '10 at 21:57

6 Answers6

15

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!

retronym
  • 53,652
  • 11
  • 151
  • 168
  • This looks great, but you are returning Unit up there... What are we limited to do in that return? Do we have to be able to inver types just from the declaration of that apply? – dividebyzero May 04 '15 at 17:19
9

In the case where we have have only 2 overloads, we can simplify Landei's answer and avoid the need to define our own implicit, by using scala.Predef.DummyImplicit which is automatically imported into every scope for you.

class Bar {
  def foo[A](xs: A*) { xs.foreach(println) }
  def foo[A, B](xs: (A, B)*)(implicit s:DummyImplicit){
    xs.foreach(x => println(x._1 + " - " + x._2))
  }
}
Community
  • 1
  • 1
Ken Bloom
  • 52,354
  • 11
  • 101
  • 164
  • 2
    Actually, this isn't limited to just 2 overloads. It works with an arbitrary number of overloads, as long as each overload has a different number of DummyImplicit parameters. – Aaron Novstrup Aug 13 '10 at 21:37
  • Thought you might be interested -- I posted about this on the scala-user list: http://scala-programming-language.1934581.n4.nabble.com/disambiguation-of-double-definition-resulting-from-generic-type-erasure-td2327664.html – Aaron Novstrup Aug 17 '10 at 01:22
  • One-liner fix (in fact, just two words), this is really sweet. Wondering what "proper" fix Scala should have to avoid the need for a workaround. – bjfletcher Jun 10 '15 at 23:47
4

If you don't mind loosing the possibility of calling foo with zero arguments (an empty Seq, if you like), then this trick can help:

def foo[A](x: A, xs: A*) { x::xs.foreach(println) }
def foo[A, B](x: (A, B), xs: (A, B)*) { (x::xs.toList).foreach(x => println(x._1 + " - " + x._2)) }

I can not check if it works now (not even if it compiles), but I think the main idea is quite easy to understand: the type of the first parameter won't be erased, so the compiler can make the difference based on that.

Unfortunately it's also not very convenient if you already have a Seq and you want to pass it to foo.

Sandor Murakozi
  • 4,232
  • 21
  • 27
3
class Bar {
    def foo[A](xs: A*) { xs.foreach{
       case (a,b) => println(a + " - " + b)
       case a => println(a)}
    }
}

This will allow

bar.foo(1,2)
bar.foo(1->3,2->4)

But also allow

bar.foo(1->2,5)
Ken Bloom
  • 52,354
  • 11
  • 101
  • 164
3

This seems less complicated than retronym's method, and is a slightly less verbose (albeit less general) version of Ken Bloom's DummyImplicit solution:

class Bar {
   def foo[A : ClassManifest](xs: A*) = { xs.foreach(println) }

   def foo[A : ClassManifest, B : ClassManifest](xs: (A, B)*) = { 
      xs.foreach(x => println(x._1 + " - " + x._2)) 
   }

   def foo[A : ClassManifest, 
           B : ClassManifest, 
           C : ClassManifest](xs: (A, B, C)*) = {
      xs.foreach(x => println(x._1 + ", " + x._2 + ", " + x._3))
   }
}

This technique can also be used if you have two overloads with the same number of type parameters:

class Bar {
   def foo[A <: Int](xs: A*) = { 
      println("Ints:"); 
      xs.foreach(println) 
   }

   def foo[A <: String : ClassManifest](xs: A*) = {
      println("Strings:");
      xs.foreach(println)
   }
}
Community
  • 1
  • 1
Aaron Novstrup
  • 20,477
  • 7
  • 67
  • 107
  • I don't think it's more general, because it depends on the fact that the different overloads have different numbers of generic parameters. This technique wouldn't work for an ambiguity between `foo(xs:Int*)` and `foo(xs:String*)`. – Ken Bloom Aug 12 '10 at 13:39
  • It's more general in the sense that it's not limited to 2 overloads. It also covers any set of 2 overloads, provided that you omit the context bound on one of the overloads. – Aaron Novstrup Aug 12 '10 at 15:00
  • I guess it works in any situation where you can pass different numbers of `ClassManifests`. On the other hand, you could do that with different numbers of `DummyImplicits` as well. – Ken Bloom Aug 13 '10 at 19:56
  • @Ken I hadn't thought of using multiple `DummyImplicits`. Good point. I retract my generality assertion. :) – Aaron Novstrup Aug 13 '10 at 20:16
  • I hadn't thought of it either until I started thinking about what made the `ClassManifest` solution work. – Ken Bloom Aug 13 '10 at 21:30
2

There is another hacky way to get this working: Glue an unrelated implicit argument on one of the methods:

class Bar {
    def foo[A](xs: A*) { xs.foreach(println) }
    def foo[A, B](xs: (A, B)*)(implicit s:String) { xs.foreach(x => println(x._1 + " - " + x._2)) }
}

implicit val s = ""

new Bar().foo(1,2,3,4)
//--> 1
//--> 2
//--> 3
//--> 4
new Bar().foo((1,2),(3,4))
//--> 1 - 2
//--> 3 - 4
Landei
  • 52,346
  • 12
  • 89
  • 188