Why there is a different error message on using += and a=x+y on a val variable? - scala

I had defined a val variable (t), and an array(m) with Int values and then tried to perform sum of all elements of array using for loop in two ways:
case1. Using += (Error message: value += is not a member of Int )
case2. Using a=x+y way (Error message: reassignment to val )
Error is expected in this case as I'm trying to re-assign a new value to a val variable but why there is different error message in case1 and case2?
scala> val t = 0
t: Int = 0
scala> val m = Array(1,2,3,4,5,6,7)
n: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7)
case1:
scala> for(e<-m) t+=e
<console>:28: error: value += is not a member of Int
for(e<-m) t+=e
^
case2:
scala> for(e<-m) t=t+e
<console>:28: error: reassignment to val
for(e<-m) t=t+e
^

Consider the desugared version of t += 42 when t is a val:
t.$plus$eq(42)
Note how there is no assignment happening, instead, it is simply a method call on t. Contrast this with desugared version of t += 42 when t is a var and it does not have += method available
t = t.$plus(42)
Here we see an assignment statement. Therefore, as there is no assignment happening in the case of t+=e where t is a val, the error message does not indicate reassignment to val, instead it is complaining about missing method +=, that is, $plus$eq, on t.

Related

Unable to create a variable of an Enumeration

I have created an Enumeration as follows:
scala> object J extends Enumeration {
| type J = Value
| val Fail, Success = Value
| }
defined object J
Question 1 - I tried to create a variable of its type but got the following error. Why?
scala> val j:J
<console>:11: error: not found: type J
val j:J
^
<console>:11: error: only classes can have declared but undefined members
val j:J
^
Question 2 - I could create a variable as follows. I suppose the Fail's value is actually 0. How could I print 0?
scala> val j = J.Fail
j: J.Value = Fail
scala> println(j)
Fail
You are using the wrong syntax to assign a type variable, you should do:
val j = J
j: J.type = J
Regarding the value, Fail and Sucess has no value apart from its own name, if you want to assign a value to them you should use this syntax:
object J extends Enumeration {
type J = Value
val Fail = Value(0)
val Success = Value(1)
}
Then you can access to it ussing its id property
scala> j.id
res: Int = 0
scala> j
res: J.Value = Fail

Multiple constants assignment in Scala

Any idea why I can assign multiple variables using a tuple, but not multiple constants?
scala> val (myVar1, myVar2) = (1, 2)
myVar1: Int = 1
myVar2: Int = 2
scala> val (MyConst1, MyConst2) = (1, 2)
<console>:7: error: not found: value MyConst1
val (MyConst1, MyConst2) = (1, 2)
^
<console>:7: error: not found: value MyConst2
val (MyConst1, MyConst2) = (1, 2)
^
I thought actually it is just a matter of naming convention, but this seems like Scala treats identifiers with upper/lower first letter differently.
As Samar suggests, in his comment, since you are declaring multiple variables, pattern matching is involved and those Uppercase identifiers are actually treated as Classes which, as your REPL errors suggest, cannot be found.
scala> var (Const1, Const2) = (1, 2)
<console>:7: error: not found: value Const1 // <- Const1 class not found
var (Const1, Const2) = (1, 2)
^
<console>:7: error: not found: value Const2 // <- Const2 class not found
var (Const1, Const2) = (1, 2)
^
scala> var (const1, const2) = (1, 2) // <- const1, const2 not treated as classes in this case
const1: Int = 1
const2: Int = 2
Pattern matching section mentions varid (look at e.g. Simple Pattern). Now in the syntax summary varid is defined as
lower ::= ‘a’ | … | ‘z’ // and Unicode category Ll
...
varid ::= lower idrest

Scala - Cannot resolve symbol - Looping through map

I am doing programming in scala for looping through map.
Below is my code which works fine.
val names = Map("fname" -> "Robert", "lname" -> "Goren")
for((k,v) <- names ) println(s"Key: $k, Value : $v")
When looping through the map, if I give (K,V) instead of (k,v), the program is not compiling. It gives cannot resolve symbol error.
Below is my for loop -
for((K,V) <- names ) println(s"Key: $K, Value : $V")
I am executing this program in IntelliJ IDEA 15 scala worksheet.
Can anyone please explain the reason for this error.
It doesn't compile for the same reason this code doesn't compile:
val (A,B) = (1,2)
// error: not found: value A
// error: not found: value B
but this does compile:
val (a,b) = (1,2)
// a: Int = 1
// b: Int = 2
Constant names should be in upper camel case. That is, if the member
is final, immutable and it belongs to a package object or an object,
it may be considered a constant
Method, Value and variable names should be in lower camel case
Source: http://docs.scala-lang.org/style/naming-conventions.html

Scala HashMap: doesn't += reassign to left hand side?

I am reading Seven Languages in Seven Weeks to get the taste of different programming paradigm. In the chapter about Scala, I found out that collection are immutable (at least the one from scala.collection.immutable).
However, there is an example which confuses me:
scala> val hashMap = HashMap(0->0)
scala> hashMap += 1->1
scala> hashMap
res42: scala.collection.mutable.HashMap[Int,Int] = Map(1 -> 1, 0 -> 0)
but
scala> map = map + 2->2
<console>:9: error: reassignment to val
map = map + 2->2
Is it += reassigning an immutable collection? How is that that += can reassign a val HashMap while = fails?
Moreover, I tried out with other collections (List and Map) and with "primitive" (Int) and += fails with the reassignment error. How are HashMaps special? I do not read anything particular in the Scala API and I cannot find a definition for += operator (I am assuming it to be an operator and not a function even in Scala, as well as in C++ or Java).
Sorry for the dumb question, but since I am new to Scala I am having difficulties finding resources by myself.
You're right that this works with a var, where the compiler can take
hashMap += 1->1
and desugar it to
hashMap = hashMap + 1->1
But there's another possibility too. If your hashMap is of the type scala.collection.mutable.hashMap then it directly calls the += method defined on that type:
hashMap.+=(1->1)
No val is reassigned, the map just internally mutates itself.
hashMap in code sample is collection.mutable.HashMap[Int,Int]. Note the mutable package.
There is a mutable version for many scala collections.
And there is method with name += in mutable.HashMap.
There are two types of collections in Scala: Mutable and Immutable
Mutable: http://www.scala-lang.org/api/2.10.3/index.html#scala.collection.mutable.package
Immutable: http://www.scala-lang.org/api/2.10.3/index.html#scala.collection.immutable.package
What you used definitely belongs to mutable category and hence can be reassigned. In fact, if you try this:
val hashMap = scala.collection.immutable.HashMap(0->0)
hashMap += (1->1)
you'll get same error
Unlike some other languages, x += y doesn't always compile into x = x + y in Scala. hashMap += 1->1 is actually an infix method call (+= is a valid method name in Scala), which is defined in mutable.HashMap class.
Your first example uses mutable HashMap, as you can see in the last line. Immutable HashMap doesn't have += method.
Note that for a mutable HashMap
scala> val map = scala.collection.mutable.HashMap[Int,Int](0 -> 0)
map: scala.collection.mutable.HashMap[Int,Int] = Map(0 -> 0)
its contents can be changed without using val or var,
scala> map += 1->1
res1: map.type = Map(1 -> 1, 0 -> 0)
scala> map += 2->2
res2: map.type = Map(2 -> 2, 1 -> 1, 0 -> 0)
scala> map
res3: scala.collection.mutable.HashMap[Int,Int] = Map(2 -> 2, 1 -> 1, 0 -> 0)
However for an immutable HashMap declared with val
scala> val imap = scala.collection.immutable.HashMap[Int,Int](0 -> 0)
imap: scala.collection.immutable.HashMap[Int,Int] = Map(0 -> 0)
we cannot for instance add new pairs,
scala> imap += 1->1
<console>:10: error: value += is not a member of scala.collection.immutable.HashMap[Int,Int]
imap += 1->1
^
However we can create a new HashMap from the original and add a new pair,
scala> val imap2 = imap.updated(1,1)
imap2: scala.collection.immutable.HashMap[Int,Int] = Map(0 -> 0, 1 -> 1)
Even so, an immutable HashMap declared with var
scala> var imap = scala.collection.immutable.HashMap[Int,Int](0 -> 0)
imap: scala.collection.immutable.HashMap[Int,Int] = Map(0 -> 0)
allows for updating the contents,
scala> imap += 1->1
scala> imap
res11: scala.collection.immutable.HashMap[Int,Int] = Map(0 -> 0, 1 -> 1)

Adding to scala map within for loop and conditional statement

I'm getting an error message of "error: type arguments [Any] do not conform to trait Cloneable's type parameter bounds [+A <: AnyRef]," which I can't make heads or tails of.
Specifically,
var M = mutable.Map[Int, mutable.Set[Int]]()
for(i <- 1 to 100; j <- 1 to 100) {
if(!M.contains(i)) {M += i -> mutable.Set[Int](j)}
else {M(i) += j}
}
(I'm actually trying something more complicated, but this is the error generating code tweaked and simplified to a minimum)
And the last line of the above code generates the error message. If I strip it down further
for(i <- 1 to 100; j <- 1 to 100) {
if(!M.contains(i)) {M += i -> mutable.Set[Int](j)}
}
it works!
How do I make the above code work?
Digal diagnosed the problem (failure to unify the types of the if-else branches) and it looks like a compiler bug. Here's a further simplified case that will give an error in the REPL, after a lengthy compilation time,
if (true) {
null: collection.mutable.Map[Int, Int]
} else {
null: collection.mutable.Set[Int]
}
In the meantime, you can get your code to compile with an explicit type sprinkled somewhere in the if-else statement,
for(i <- 1 to 100; j <- 1 to 100) {
if(!M.contains(i)) {M += i -> mutable.Set[Int](j)}
else {M(i) += j}: Unit
}
I filed an issue here: https://issues.scala-lang.org/browse/SI-4938
I've reduced your example even further:
scala> if(!M.contains(1)) {M += 1 -> mutable.Set[Int](1)} else {M(1) += 1};
<console>:9: error: type arguments [Any] do not conform to trait Cloneable's type parameter bounds [+A <: AnyRef]
val res17 =
^
The problem seem to occur when compiler tries to find common return type for both branches:
The first one is
scala> M += 1 -> mutable.Set[Int](1)
res19: scala.collection.mutable.Map[Int,scala.collection.mutable.Set[Int]] = ...
And the "else" part is
scala> M(1) += 1
res18: scala.collection.mutable.Set[Int] = Set(1)
If I add a return value to the end of this expression, REPL eats it without errors:
scala> if(!M.contains(1)) {M += 1 -> mutable.Set[Int](1)} else {M(1) += 1}; println("hello")
hello
Because the expression's return type is Unit.