Difficulty defining multiple methods with same name


#1

I have another confusing issue similar to the one reported here: function already defined

Can someone help me understand the limitation?

I have three definitions of a method binSearch, each with different types. Both IntelliJ and the scala compiler complain about one of these but neither of the others. Interesting, InteliJ and scalac give different errors. Here are the definitions.

object BinarySearch {

  def binSearch(left:Double, right:Double, f:Double=>Double, target:Double, threshold:Double,maxDepth:Int):Option[Double] = {
    binSearch(left, right, x=>f(x)-target, threshold,maxDepth)
  }
  def binSearch(left:Double, right:Double, f:Double=>Double, threshold:Double ,maxDepth:Int):Option[Double] = {
    ???
  }

  def binSearch(left:Double, right:Double, f:Double=>Boolean, delta:Double, maxDepth:Int):Option[Double] = {
    // takes a function, f, for which f(left) = false, and f(right) = true,
    // finds an x such that f(x)=false, and f(x+threshold)= true
    ???
  }

  def main(argv:Array[String]) = {

  }
}

IntelliJ complains that the call to binSearch on line 4:

whereas, scalac, complains about a double definition.

Error:(15, 7) double definition:
def binSearch(left: Double,right: Double,f: Double => Double,threshold: Double,maxDepth: Int): Option[Double] at line 11 and
def binSearch(left: Double,right: Double,f: Double => Boolean,delta: Double,maxDepth: Int): Option[Double] at line 15
have same type after erasure: (left: Double, right: Double, f: Function1, threshold: Double, maxDepth: Int)Option
  def binSearch(left:Double, right:Double, f:Double=>Boolean, delta:Double, maxDepth:Int):Option[Double] = {

#2

This time compiler message tells it straight. Both arguments f: Double => Double and f: Double => Boolean are erased to the same thing f: Function1. You can’t define two methods of the same name that have the same parameter types after erasure.

f: A => B is syntactic sugar for f: Function1[A, B]. Erasure removes generic parameters so you’re left with f: Function1.


#3

Ouch. So by that I can’t have two methods one for List[String] and the other for List[Int]? Isn’t this a serious limitation? Well I guess I don’t have to like it; I just have to understand it.


#4

Only overloading a method has this limitation. If you give up on overloading methods, i.e. if you start naming them differently, then the problem will be gone.

In my opinion, overloading frequently makes code less readable if parameters (of the same index in parameters list) are used for different purposes.

In this case instead of three methods named binSearch I would opt for method names like byPredicate, byNumber, etc and thus avoid overloading.

If you really want to do overloading here then you can e.g. combine it with default parameters. Then you’re left with:

  def binSearch(left:Double, right:Double, f:Double=>Double, threshold:Double,maxDepth:Int, target:Double = 0.0):Option[Double]

  def binSearch(left:Double, right:Double, f:Double=>Boolean, delta:Double, maxDepth:Int):Option[Double]

…and the compilation error should be gone, because you have different erased signatures (first one has one more parameter).


#5

This is a really general hard limitation, and I strongly recommend getting it internalized – type parameters are erased at runtime, which means the resulting classes look alike. So you can’t use that for overloading, you can’t use it for pattern-matching, you can’t use it for isInstanceOf, etc. This is one of the reasons I recommend learning typeclasses, since the typeclass pattern is less constrained in this way.

But for this particular example, I agree with @tarsa: the problem here is really overloading. This is one of the many reasons why many of us tend to avoid overloading, and just use different names…