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

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.

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

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.

Adding to immutable HashSet

Sorry guys, I recently saw an example in "Programming in Scala", 2nd Edition on page 685, which seemed strange to me:
var hashSet: Set[C] = new collection.immutable.HashSet
hashSet += elem1
How is it possible to add something an immutable collection? I tried on REPL and it worked ok!
> scala
Welcome to Scala version 2.11.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_11).
Type in expressions to have them evaluated.
Type :help for more information.
scala> var s : Set[Int] = collection.immutable.HashSet()
s: Set[Int] = Set()
scala> s += 1324
scala> println(s)
Set(1324)
The stranger fact is that += operator is not defined in immutable.HashSet api page. Could anybody please help me understand what's going on?
Thanks.
You are not adding to the HashSet. You are assigning to hashSet, which is perfectly fine, since hashSet is a var, not a val.
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 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.
This allows something like += to work like it does in other languages but still be overridden to do something different.
Observe this:
scala> var hashSet: Set[Int] = new collection.immutable.HashSet
hashSet: Set[Int] = Set()
scala> val set2 = hashSet + 1234
set2: scala.collection.immutable.Set[Int] = Set(1234)
scala> set2
res20: scala.collection.immutable.Set[Int] = Set(1234)
scala> hashSet
res21: Set[Int] = Set()
So nothing gets added to the immutable hashSet. hashSet is the same as it was when constructed. + returns a new set altogether and the original set is unchanged.
When you do hashSet += 1234, it is a scala shorthand for (note no method += exists in HashSet):
val temp = hashSet + 1234
hashSet = temp
+= will work for any class which follows this protocol. In short when you do a += 12. a must have a method + which returns the same type as a's and a should be assignable (i.e. a var. It does not work for val. Try this: val i = 23; i+=1).
Short answer
You have a var so you can reassign to it. So += in this case will be translated to
hashSet = hashSet + elem
just like other types, as long as + is defined on them
var i = 0
i += 1
i = i + 1
Details
immutable.HashSeth has + method which
Creates a new set with an additional element, unless the element is
already present.
according to docs.
There is no += method defined in this class, so += will be a synthetic method given to you by compiler which acts as an operator simply calling the + method on the left operand by passing the right operand and assigning the result back to the left operand.

How does foo(a)(b) += x work in Scala?

I have been counting occurences with a mutable map:
var bar = collection.mutable.Map[Int, Int]().withDefaultValue(0)
Now bar(a) += b works just fine, no matter if the key a is present in bar already or not (in which case it will then be added).
I tried the same thing with a mutable map of mutable maps:
var foo = collection.mutable.Map[Int, collection.mutable.Map[Int, Int]]().
withDefaultValue(collection.mutable.Map().withDefaultValue(0))
How does foo(a)(b) += x look without syntactic sugar?
Using What are all the instances of syntactic sugar in Scala? I would assume it expands to:
foo.apply(a).put(b, foo.apply(a).apply(b) + x)
But why does this not update foo itself accordingly like in the introductory example (i.e. foo will not have a dedicated value for key a if it wasn't present before)?
Edit: As Perseids pointed out, foo(a)(b) += x will change the mutable default value. Is this a desired feature?
Using getOrElseUpdate as suggested by DaoWen seems to be the best way to overcome both problems. But while that works well for functions of type Int => Int => Int it becomes really cumbersome for functions of type Int => Int => Int => Int => Int. So I'm still happy for any suggestions!
This is actually an issue with withDefaultValue, not the += operator. Your first withDefaultValue for foo returns a new mutable map if the given key does not exist. When you do the lookup foo(a)(b), if foo(a) doesn't exist, then it returns a new map, which we'll call tmp. foo(a)(b) += x then essentially expands to this:
val tmp = foo(a)
tmp(b) += x
The problem is that only tmp is being updated by +=, not foo. So your update is happening on tmp, but tmp gets thrown away after the call because it's never stored anywhere.
If you want your parent map to get updated, you might want to look into using getOrElseUpdate instead of relying on withDefaultValue.
Note: As Perseids points out in the comments below, withDefaultValue takes a by-value parameter. This means that every time you get an unset key from your map, it's going to return the same mutable map instance! This is yet another reason you should think about using getOrElseUpdate (which uses a by-name parameter), or at least withDefault, which takes a function. (That's all assuming that you actually want different map instances for each slot in your map...)
How does foo(a)(b) += x look without syntactic sugar?
It depends on whether or not the object returned by foo(a)(b) has a method named += or not. If it does, then it's equivalent to:
foo.apply(a).apply(b).+=(x)
If it doesn't, then it's equivalent to:
foo.apply(a).update(b, foo.apply(a).apply(b).+(x))
Except without the duplicate evaluation of foo.apply(a) (I think.)

Declaring multiple variables in Scala

I'd like to use val to declare multiple variable like this:
val a = 1, b = 2, c = 3
But for whatever reason, it's a syntax error, so I ended up using either:
val a = 1
val b = 2
val c = 3
or
val a = 1; val b = 2; val c = 3;
I personally find both options overly verbose and kind of ugly.
Is there a better option?
Also, I know Scala is very well thought-out language, so why isn't the val a = 1, b = 2, c = 3 syntax allowed?
The trivial answer is to declare them as tuples:
val (a, b, c) = (1, 2, 3)
What might be interesting here is that this is based on pattern matching. What is actually happens is that you are constructing a tuple, and then, through pattern matching, assigning values to a, b and c.
Let's consider some other pattern matching examples to explore this a bit further:
val DatePattern = """(\d{4})-(\d\d)-(\d\d)""".r
val DatePattern(year, month, day) = "2009-12-30"
val List(rnd1, rnd2, rnd3) = List.fill(3)(scala.util.Random.nextInt(100))
val head :: tail = List.range(1, 10)
object ToInt {
def unapply(s: String) = try {
Some(s.toInt)
} catch {
case _ => None
}
}
val DatePattern(ToInt(year), ToInt(month), ToInt(day)) = "2010-01-01"
Just as a side note, the rnd example, in particular, may be written more simply, and without illustrating pattern matching, as shown below.
val rnd1, rnd2, rnd3 = scala.util.Random.nextInt(100)
Daniel's answer nicely sums up the correct way to do this, as well as why it works. Since he already covered that angle, I'll attempt to answer your broader question (regarding language design)...
Wherever possible, Scala strives to avoid adding language features in favor of handling things through existing mechanisms. For example, Scala doesn't include a break statement. However, it's almost trivial to roll one of your own as a library:
case object BreakException extends RuntimeException
def break = throw BreakException
def breakable(body: =>Unit) = try {
body
} catch {
case BreakException => ()
}
This can be used in the following way:
breakable {
while (true) {
if (atTheEnd) {
break
}
// do something normally
}
}
(note: this is included in the standard library for Scala 2.8)
Multiple assignment syntaxes such as are allowed by languages like Ruby (e.g. x = 1, y = 2, z = 3) fall into the category of "redundant syntax". When Scala already has a feature which enables a particular pattern, it avoids adding a new feature just to handle a special case of that pattern. In this case, Scala already has pattern matching (a general feature) which can be used to handle multiple assignment (by using the tuple trick outlined in other answers). There is no need for it to handle that particular special case in a separate way.
On a slightly different aside, it's worth noting that C's (and thus, Java's) multiple assignment syntax is also a special case of another, more general feature. Consider:
int x = y = z = 1;
This exploits the fact that assignment returns the value assigned in C-derivative languages (as well as the fact that assignment is right-associative). This is not the case in Scala. In Scala, assignment returns Unit. While this does have some annoying drawbacks, it is more theoretically valid as it emphasizes the side-effecting nature of assignment directly in its type.
I'll add one quirk here, because it hit myself and might help others.
When using pattern matching, s.a. in declaring multiple variables, don't use Capital names for the variables. They are treated as names of classes in pattern matching, and it applies here as well.
val (A,B)= (10,20) // won't work
println(A)
Error message does not really tell what's going on:
src/xxx.scala:6: error: not found: value A
val (A,B)= (10,20)
^
src/xxx.scala:6: error: not found: value B
val (A,B)= (10,20)
^
src/xxx.scala:7: error: not found: value A
println(A)
^
I thought `-ticking would solve the issue but for some reason does not seem to (not sure, why not):
val (`A`,`B`)= (10,20)
println(A)
Still the same errors even with that.
Please comment if you know how to use tuple-initialization pattern with capital variable names.
If all your variables are of the same type and take same initial value, you could do this.
val a, b, c: Int = 0;
It seems to work if you declare them in a tuple
scala> val (y, z, e) = (1, 2, 45)
y: Int = 1
z: Int = 2
e: Int = 45
scala> e
res1: Int = 45
Although I would probably go for individual statements. To me this looks clearer:
val y = 1
val z = 2
val e = 45
especially if the variables are meaningfully named.