Strange limitation when importing from different scopes

Recently I ran into the following issue.

In one package object I have a method to linearly interpolate between numbers:

def interpolate(t: Double, a: Double, b: Double): Double = a * (1.0 - t) + b * t

And in another package object I have a method to linearly interpolate between vectors (where Vector is my own 3D vector class, which contains a * method to scale a vector with a number and a + method to add two vectors). It looks a lot like the method for numbers, but with different argument and return types:

def interpolate(t: Double, a: Vector, b: Vector): Vector = a * (1.0 - t) + b * t

I have another source file where I import both the package objects, so both interpolate methods are in scope. I’m trying to use one of the methods:

import org.jesperdj.scalaray._              // contains the interpolate method for numbers
import org.jesperdj.scalaray.vecmath._      // contains the interpolate method for vectors

val d: Double = interpolate(0.8, 1.5, 6.5)  // should call the first interpolate method

Unexpectedly, this doesn’t work – the Scala compiler (at least of version 2.8.0.RC7) gives you the following error message:

Example.scala:19: error: reference to interpolate is ambiguous;
it is imported twice in the same scope by
import org.jesperdj.scalaray.vecmath._
and import org.jesperdj.scalaray._
                val d: Double = interpolate(0.8, 1.5, 6.5)
                                ^
one error found

Very strange – the methods have different signatures, and I’m clearly calling the one with Double arguments, so there is really no ambiguity here. If I just define the two methods in the same scope, it works without a problem:

object Example {
  def interpolate(t: Double, a: Double, b: Double): Double = a * (1.0 - t) + b * t
  def interpolate(t: Double, a: Vector, b: Vector): Vector = a * (1.0 - t) + b * t

  def main(args: Array[String]) {
    // No problem!
    val d: Double = interpolate(0.8, 1.5, 6.5)
  }
}

I entered a ticket into the Scala bug tracking system, but it turns out I’m not the first one to discover this, there was already another ticket describing the same issue. Unfortunately, the Scala team decided that this will not be fixed, because it requires changes in the Scala specifications, which is a lot of work.

You can do something similar in Java, with import static. I tried it out and in Java it works without a problem:

import static example.Scope1.*;
import static example.Scope2.*;

public class Example {
    public static void main(String[] args) {
        // No problem!
        double d = interpolate(0.8, 1.5, 6.5);
    }
}

(where Scope1 and Scope2 are two classes that contain the two public static interpolate methods).

6 Comments

  1. Off topic, but one thing I do notice is that your interpolate method would be an excellent candidate for currying.

    def interpolate(a: Double, b: Double)(t:Double): Double = a * (1.0 – t) + b * t

    val interpolation = interpolate(2.0, 4.0)

    println(interpolation(0.5)) //prints 3.0

    Just a thought.

    • Is there any substantive difference between partial functions and curried ones? E.g. is it any different to do this:

      val interpolation = interpolate(2.0, 4.0, _)
      println(interpolation(0.5))

      Honest question.

  2. Really annoying. It does make you wonder how complicated the Scala compiler is, if minor issues like this is a problem.

    • The comment by Martin Odersky in ticket 2551 suggests that this isn’t only a case of fixing the problem in the compiler – the language specification would have to be modified to allow this, and for that to happen a SID (Scala Improvement Document) first has to be written.

      Fixing bugs in specifications is often much harder than fixing bugs in code, in fact it might be easy to fix in the compiler, but because it has to be precisely specified in a document it will still be a lot of work.

      • Yes, I did notice that, but I find it odd (and I’d be nice if someone could post a reference to the spec) for a spec to specifically disallow overloaded methods. Typically, there’s mention of a signature, and the signature in this case is unique and unambiguous.

Comments are closed.