Why does Scala define a "+=" operator for Short and Byte types? - scala

Given the following scala code:
var short: Short = 0
short += 1 // error: type mismatch
short += short // error: type mismatch
short += 1.toByte // error: type mismatch
I don't questioning the underlying typing - it's clear that "Short + value == Int".
My questions are:
1. Is there any way at all that the operator can be used?
2. If not, then why is the operator available for use on Short & Byte?
[And by extension *=, |= &=, etc.]

The problem seems to be that "+(Short)" on Short class is defined as:
def +(x: Short): Int
So it always returns an Int.
Given this you end up not being able to use the += "operator" because the + operation evaluates to an Int which (obviously) can not be assigned to the "short" var in the desugared version:
short = short + short
As for your second question, it is "available" because when the scala compiler finds expressions like:
x K= y
And if x is a var and K is any symbolic operator and there is K method in x then the compiler translates or "desugar" it to:
x = x K y
And then tries to continue compilation with that.

Related

When does a += b become a = a + b in Scala?

Scala only sometimes desugars
a += b
to
a = a + b
but not always. For example, some mutable collections define a += method, where instead it becomes
a.+=(b)
Is this behaviour
entirely determined by whether there is a suitable += method on a? (incl. are there any other examples of this behaviour?)
independent of whether the objects are val or var?
Relevant example
Adapted from Programming in Scala, in
var s = Set("a", "b")
s += "c"
In this case, the second line of code s += "c" is essentially shorthand for:
s = s + "c"
When does a += b become a = a + b in Scala?
When there is no applicable += method, there is an applicable + method and a is assignable (that is, it's a var or there's an a_= method).
Or as the spec puts it:
The re-interpretation occurs if the following two conditions are fulfilled.
The left-hand-side l does not have a member named +=, and also cannot be converted by an implicit conversion to a value with a member named +=.
The assignment l = l + r is type-correct. In particular this implies that l refers to a variable or object that can be assigned to, and that is convertible to a value with a member named +.
Is this behaviour
entirely determined by whether there is a suitable += method on a?
independent of whether the objects are val or var?
Not quite. If there is a suitable += method, it will be called regardless of any other factors (such as a being assignable). But when there isn't, the other factors determine whether it's desugared or you get an error message.
Note that the error message you get is different than the one you'd get from the desugared version: When the criteria for the desugaring don't apply, you get an error message that tells you "+= is not a member of ...", plus an explanation why the desugaring couldn't be applied (such as "receiver is not assignable" or the type error you'd get from a + b if a + b would produce a type error).
In my first answer I was a little hasty, my apologies. After researching a little and after read better the #sepp2k's answer and comment, I came to the conclusion that some classes in Scala implements the += method, and other just the + method, I played a little in With some Scala code and for instance:
//Set, Int, Double, String implements the "+" method,
//and then "+=" is syntactic sugar of a = a + b
var set = Set("a", "b")
set += "c"
var num = 3
num += 2
var str = "43"
str += 5
var l = List()
l += "someString"
// As you mention, MutableList implements "+=" method, and when you do
// mutL += 4, is the same as call the method mutL.+=
var mutL = new mutable.MutableList[Int]
mutL += 4

Scala behaviour when assigning literals or variables to Char

In Scala, if I have a variable declaration, e.g.
var x: Char = 'a'
If I then try and update this character by adding 1, e.g.
x = x + 1
I get a compilation error: Type mismatch, found Int required Char. However, I can do this without a compilation error:
x = 'a' + 1
I'm guessing this has something to do with literal values vs objects, however, I'm trying to get my head around the exact behaviour. You can clearly assign a literal integer to a Char, e.g. 97, and you can also assign the result of 97-32. However if I say 97-32+5 then I get a type mismatch error. At what point does the compiler differentiate between an expression that results in a literal vs one that result in an object?
Assignment is the key here.
Look at the following REPL session:
alex#POSITRON ~ $ scala
Welcome to Scala version 2.11.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_131).
Type in expressions to have them evaluated.
Type :help for more information.
scala> val x:Char = 'a'
x: Char = a
scala> x + 1
res0: Int = 98
scala> var y:Char = 'a'
y: Char = a
scala> y + 1
res1: Int = 98
scala> y = y + 1
<console>:8: error: type mismatch;
found : Int
required: Char
y = y + 1
^
scala>
So as you can see unless you try to reassign the variable values everything goes fine. When you write 'a'+1 or x + 1 it gets converted to Int.
So when you finally try to x = x + 1 reassign then you are trying to assign Int value to the Char variable. This explain why compilation error occurs.
In the Char companion object there is implicit def char2int(x: Char): Int method.
I think in var x:Char = 'a' + 1 the first thing which happens is invocation of this method to convert 'a' to 97. Then 97 is added 1, both as Ints. Then the variable x gets instantiated in the same way as in val y:Char = 98. This I think explains how variable initialization works.
There's a special rule for typing literals in the spec.
However, if the expected type pt of a literal in an expression is
either Byte, Short, or Char and the integer number fits in the numeric
range defined by the type, then the number is converted to type pt and
the literal's type is pt.
That lets you write:
scala> 97 : Char
res0: Char = a
scala> 97 + 1 : Char
res1: Char = b
Sneakily, they mean constant expressions here. But the definition of constant expression is platform-specific, so technically res1 could also fail to compile, if it were not constant-folded.
A related question about constant folding in scaladoc shows the weird loss of type checking under scaladoc:
$ ~/scala-2.12.3/bin/scaladoc -d /tmp folded.scala
folded.scala:9: error: type mismatch;
found : Int
required: Char
x = 'a' - 32 + 5
^
model contains 4 documentable templates
one error found

Where is Scala's += defined in the context of Int?

Just starting out with Scala
var c = 0
c += 1 works
c.+= gives me error: value += is not a member of Int
Where is the += defined?
Section 6.12.4 Assignment Operators of the Scala Language Specification (SLS) explains how such compound assignment operators are desugared:
l ω= r
(where ω is any sequence of operator characters other than <, >, ! and doesn't start with =) gets desugared to
l.ω=(r)
IFF l has a member named ω= or is implicitly convertible to an object that has a member named ω=.
Otherwise, it gets desugared to
l = l.ω(r)
(except l is guaranteed to be only evaluated once), if that typechecks.
Or, to put it more simply: the compiler will first try l.ω=(r) and if that doesn't work, it will try l = l.ω(r).
This allows something like += to work like it does in other languages but still be overridden to do something different.
Actually, the code you've described does work.
scala> var c = 4
c: Int = 4
scala> c.+=(2) // no output because assignment is not an expression
scala> c
res1: Int = 6
I suspect (but can't say for sure) that it can't be found in the library because the compiler de-surgars (rewrites) it to c = c.+(1), which is in the library.

Where is += method located for int in scala

Is +=(or any assignment operators) a method in scala for Int type.
For example,
var x=5
x+=1
Here I am able to use += method only when it is a variable.
I am not able to do,
5+=1
Does scala compiler considers this method as a special case?
Why it is not available in scala.Int class?
There is no += method, it is expanded to x = x + 1 by the compiler. This is detailed in the specification:
6.12.4 Assignment Operators
Let's consider an assignment operator such as += in an infix operation
l += r, where l, r
are expressions. This operation can be re-interpreted as an operation
which corresponds to the assignment
l = l + r
except that the operation's left-hand-side l is evaluated only once.
The re-interpretation occurs if the following two conditions are
fulfilled.
The left-hand-side l does not have a member named +=, and also cannot be converted by an implicit conversion to a value with a member
named +=.
The assignment l = l + r is type-correct. In particular this implies that l refers to a variable or object that can be assigned to,
and that is convertible to a value with a member named +.

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

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
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...
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.
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.
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.
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 scala-user#listes.epfl.ch or by visiting n4.nabble.com/Scala-User-f1934582.html. If it's a bug, that's where it will be noticed and fixed.