Help me understand this compiler error

Can someone please help me understand why IntelliJ (and perhaps scalac) thinks there is an error in matching+(src,dst)

class confusing {
  type Station  = Int
  type Time     = Int
  type Leg      = (Station,Station,Time)
  type Matching = Set[(Station,Station)]
  case class Routing(path:List[Leg], stations:Set[Station])

  def augmentedPathToMatching(routing:Routing):Matching = {
    def recur(legs:List[Leg],count:Int,matching:Matching):Matching = {
      legs match {
        case Nil => matching
        case (src,dst,_)::legs => recur(legs,count+1,if (count % 2 == 0) matching else matching+(src,dst))
      }
    }
    recur(routing.path,1,Set())
  }
}

The variable matching is of type Matching which is Set[(Int,Int)], so I should be able to add an element to the set (non-destructively) by matching+(src,dst) as long as src and dst are both of type Int. Right? It is really hard for me as a beginner to distinguish between and error in my program design/concept, and a limitation in the scala type inferencer.

Here is the error message I receive.

The compiler interprets matching+(src, dst) as two arguments passed to the + method instead of one argument that is a pair. The correct syntax is matching+((src, dst)), though the syntactic sugar matching+(src -> dst) may look nicer.

regards,
Siddhartha

1 Like

ouch. That’s bizarre. I’m almost afraid to ask, but why does the compiler interpret matching+(src,dst) as two arguments passed the + method?

The compiler actually had a feature called auto-tupling or argument adaption, which made this syntax valid. But this caused various problems in other cases, see the discussion about removing it for several examples.

To summarize: with functions that have overloads or parameterized types, it could lead to passing a tuple, when you actually passed the wrong number of arguments. The linked thread also shortly discussed, if it should be kept for infix operations, but it has the same problems, as long as you allow infix notation to be used with methods that take more than one parameter.

From Odersky’s last comments, it looks like auto-tupling for infix may return in Dotty.

In my opinion, one of the things that makes scala interesting is that it is a seat of lots of ongoing research, trying to find good solutions in programming language design. I understand that this task is enormously difficult.

On the other hand, to add my $0.02 worth: While adding an extra set of parens is easy, and I’m happy to do it, how is the beginner supposed to figure out what that compiler error means? If the answer is, “The beginner is free and welcome to ask questions” then that’s a pretty good answer, and another thing that makes the scala experience pleasant.

The main reason that it doesn’t work is that the + method in Set is overloaded. There’s an overload def +(elem1: A, elem2: A, elems: A*): Set[A]. Because of the misfeature that Scala interprets a + (b, c) as a.+(b, c) instead of a.+((b, c)) the compiler thinks that you meant this overload instead of the one for adding a single value. The fix would be that Scala should always interpret a operator b as an application of a method that takes exactly one (explicit) argument, regardless of the shape of b. An infix operator doesn’t make any sense if the right-hand side has multiple parameters IMHO. I believe this change is indeed underway for the near future.

1 Like

That’s a largely true answer (and really, is why this forum exists). Over the ~7 years I’ve been heavily involved, we’ve been steadily getting better on that front.

But also, folks generally recognize that error messages are currently a Scala pain point. They’re trying to improve that in Dotty, and help is generally welcomed on that front, I believe…

Hello,

  • If “+” is a method name just like, say, “plus”, then matching+(src, dst), which is equivalent to matching.+(src, dst) should be equivalent to matching.plus(src, dst).

  • The decision that these are two arguments is made by the parser/lexer, not the typer. It just happens that the typer is the first to notice that something is off.

  • BTW, IntelliJ uses it’s own compiler to check code. But scalac has a very similar error message.

  • How can this be improved? Warn when an operator is called with more than one arguments? Or maybe first check whether a method even exists that takes the number of arguments it is called with, before we check for types?

Best, Oliver

@Oliver, yes I understand that the problem is more subtle that appears to the novice. BTW I find this a very interesting problem, that the lexer cannot make a good decision because it does not have the correct type information. The problem (feature?) is that scala uses the same syntax for tuple as it does for function call. If scala had used a different tuple syntax from day-1, the problem would not have occurred. [[a,b,c]] for example, or (| a, b, c |), of course (a,b,c) is much more beautiful. As in mathematics (a,b) is a very overused notation.

Ah, I was wondering why the example gave an error about the argument type, and not about the wrong number of arguments.

Then I realized that Set actually has a + operator with a variable argument list, so + with two arguments is valid. Enabling auto-tupling would not even do anything in this case.

**Welcome to Scala 2.12.8 (OpenJDK 64-Bit Server VM, Java 1.8.0_191).
Type in expressions for evaluation. Or try :help.

Set(1, 2)+(3, 4)
res1: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 4)**