Scala puts precedence on implicit conversion over "natural" operations... Why? Is this a bug? Or am I doing something wrong?
Asked Answered
E

5

9

This simple test, of course, works as expected:

scala> var b = 2
b: Int = 2

scala> b += 1   

scala> b
res3: Int = 3

Now I bring this into scope:

class A(var x: Int) { def +=(y:Int) { this.x += y } }
implicit def int2A(i:Int) : A = new A(i)             

I'm defining a new class and a += operation on it, and a convenient implicit conversion for those times when I want to add an Int to A's Int value.

I never expected this would affect the way my regular Int operations behave, when the "A" class is not at all part of the expression.

But it does:

scala> var b:Int = 0
b: Int = 0

scala> b += 1

scala> b  
res29: Int = 0

scala> b += 2

scala> b
res31: Int = 0

What seems to be happening here is that the b:Int is implicitly converted to an "A", which is not bound to any variable, and then += is invoked on it, discarding the results.

Scala seems to give high precedence the implicit conversion over the natural += behavior (compiler magic, not an actual method) that is already defined to Ints. Common-sense as well as a C++ background tells me implicits should only be invoked as a last resort, when the compilation would otherwise fail. That leads to several questions...

  • Why? Is this a bug? Is it by design?
  • Is there a work-around (other than not using "+=" for my DSL's "+=" operation)?

Thanks

Epicurus answered 21/4, 2010 at 13:0 Comment(1)
scala -Xprint:typer tells me that it's doing exactly what you said it's doing -- using an implicit conversion so that it could call +=.Dinadinah
D
0

Even withstanding Eastsun's explanation, it seems like this is a bug, and it should try the b=b+1 transformation before trying an implicit conversion for +=.

Please ask this question to the scala-user email list by emailing [email protected] or by visiting n4.nabble.com/Scala-User-f1934582.html. If it's a bug, that's where it will be noticed and fixed.

Dinadinah answered 21/4, 2010 at 13:35 Comment(0)
S
15

As others have noted, Int cannot have a += "method", because Int is immutable. What happens instead is that x += 1 is treated as a short form for x = x + 1, but only if there is no method called += that is defined on the type. So method resolution takes precedence.

Given that Scala lets you define += methods and also lets you do += on variables, could we have changed the priority of the two? I.e. try expanded += first and only if that fails search for a method named +=?

Theoretically yes, but I argue it would have been worse than the current scheme. Practically, no. There are many types in Scala's collection library that define both a + method for non-destructive addition and a += method for destructive addition. If we had switched the priority around then a call like

  myHashTable += elem

would expand to

  myHashTable = myHashTable + elem

So it would construct a new hashtable and assign this back to the variable, instead of simply updating an element. Not a wise thing to do...

Suzy answered 21/4, 2010 at 16:24 Comment(3)
But couldn't we try running myHashTable's +=, then seeing that it's not found we expand it to myHashTable = myHashTable + elem, and failing that fall back to implicit conversions?Dinadinah
This would complicate things further. And it would invalidate a class of useful refactorings, where inheritance is replaced by views.Suzy
I agree that trying to find a += method before expanding to "... = ... + ..." makes perfect sense. What I don't agree is forcing an implicit conversion just to find a += in a class which is "unrelated" to the expression at hand. Another approach would be to formally define += in Int so that it gets "found" sooner (before the implicit conversion), but use special syntax to indicate it's "not a real method" and explicitly tell the compiler to fallback to the expansion magic. BTW I'm flattered to get an answer directly from the top :) ThanksEpicurus
G
6

From Programming in Scala, Chapter 17:

Whenever you write a += b, and a does not support a method named +=, Scala will try interpreting it as a = a + b.

The class Int does not contain method +=. However class A provides += method. That might be triggering the implicit conversion from Int to A.

Gast answered 21/4, 2010 at 13:1 Comment(4)
Thanks. I'm starting to get very disappointed at the amount of "magic" that has been implemented in what was supposed to be a very "orthogonal, clean language". They should just implement += in Int and get rid of the compiler magic. I hope there will be a Scala 3.0 :)Epicurus
@Alex How would you implement += on an immutable class?Ryannryazan
@Alex Well, that's orthogonality. C++ has tens of thousands of "things", including a += method. Scala doesn't have a += method, a -= method, a ::= method, a &&= method, etc. It has just one feature, the rule above, which combines with the rest of the language to provide all sort of things. That's the beauty of orthogonality.Jayejaylene
@Ben, good point (I think C++ gets around it by letting you define += as pass-by-reference, which is not supported in the JVM). @Daniel, the += magic is breaking my DSL. I hope there would be a way to turn it off or unimport it.Epicurus
W
3

I don't think it is a bug. Actually, Int only has a "+" method but doesn't have a "+=" method. b += 1 would transform to b = b + 1 in compile time if there is not a other implicit which has a "+=" method exists.

Wersh answered 21/4, 2010 at 13:23 Comment(1)
I think, that even withstanding your explanation it's probably a bug, and it should try the transformation before trying an implicit conversion for +=Dinadinah
L
1

Scala seems to give high precedence the implicit conversion over the natural += that is already defined to Ints.

Which version of Scala are you talking about? I don't know of any version that has a += method on Int. Certainly, none of the still supported versions do, that must be some really ancient version you have there.

And since there is no += on Int, but you are calling a += method on Int, Scala tries to satisfy that type constraint via an implicit conversion.

Lepanto answered 21/4, 2010 at 14:1 Comment(1)
Edited my question to clarify Int.+= is compiler magic, not an actual method.Epicurus
D
0

Even withstanding Eastsun's explanation, it seems like this is a bug, and it should try the b=b+1 transformation before trying an implicit conversion for +=.

Please ask this question to the scala-user email list by emailing [email protected] or by visiting n4.nabble.com/Scala-User-f1934582.html. If it's a bug, that's where it will be noticed and fixed.

Dinadinah answered 21/4, 2010 at 13:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.