Why “avoid method overloading”?

Overloading makes it a little harder to lift a method to a function:

object A {
   def foo(a: Int) = 0
   def foo(b: Boolean) = 0
   def foo(a: Int, b: Int) = 0

   val function = foo _ // fails, must use = foo(_, _) or (a: Int) => foo(a)
}

You cannot selectively import one of a set of overloaded methods.

There is a greater chance that ambiguity will arise when trying to apply implicit views to adapt the arguments to the parameter types:

scala> implicit def S2B(s: String) = !s.isEmpty                             
S2B: (s: String)Boolean

scala> implicit def S2I(s: String) = s.length                               
S2I: (s: String)Int

scala> object test { def foo(a: Int) = 0; def foo(b: Boolean) = 1; foo("") }
<console>:15: error: ambiguous reference to overloaded definition,
both method foo in object test of type (b: Boolean)Int
and  method foo in object test of type (a: Int)Int
match argument types (java.lang.String)
       object test { def foo(a: Int) = 0; def foo(b: Boolean) = 1; foo("") }

It can quietly render default parameters unusable:

object test { 
    def foo(a: Int) = 0; 
    def foo(a: Int, b: Int = 0) = 1 
}

Individually, these reasons don’t compel you to completely shun overloading. I feel like I’m missing some bigger problems.

UPDATE

The evidence is stacking up.

UPDATE 2

  • You can’t (currently) use overloaded methods in package objects.
  • Applicability errors are harder to diagnose for callers of your API.

UPDATE 3

  • static overload resolution can rob an API of all type safety:
scala> object O { def apply[T](ts: T*) = (); def apply(f: (String => Int)) = () }
defined object O

scala> O((i: String) => f(i)) // oops, I meant to call the second overload but someone changed the return type of `f` when I wasn't looking...

Leave a Comment