Not sure where my assignment is going - scala

I ran into some issues today making assignments to a var field in a case class instance stored in a map. Here's a simple session in the repl demonstrating the problem:
scala> case class X(var x: Int)
defined class X
scala> val m = Map('x -> X(1))
m: scala.collection.immutable.Map[Symbol,X] = Map('x -> X(1))
scala> m
res0: scala.collection.immutable.Map[Symbol,X] = Map('x -> X(1))
scala> m('x).x = 7
scala> m
res1: scala.collection.immutable.Map[Symbol,X] = Map('x -> X(1))
scala> val x = m('x)
x: X = X(1)
scala> x.x = 7
x.x: Int = 7
scala> x
res2: X = X(7)
scala> m
res3: scala.collection.immutable.Map[Symbol,X] = Map('x -> X(7))
scala> m('x).x_=(8)
scala> m
res5: scala.collection.immutable.Map[Symbol,X] = Map('x -> X(8))
The first attempt at assignment does nothing. However, storing the instance in a val and then doing the assignment works, as does directly calling the assignment method for the field.
I'm using Scala 2.9.2.
If this is expected behavior, it would be nice if someone could explain it to me because I can't seem to make sense of it right now. If this is a bug then that would be good to know as well.
Either way, it would also be interesting to know where that first m('x).x = 7 assignment is going. I assume something is getting mutated somewhere—I just have no idea what that something could be.
Update: It looks like this only happens in the repl. I just tried compiling the code and the assignment happens as expected. So, what is the repl doing to my assignment?

This seems to be a bug. If one executes this with a 2.10 nightly an error message is thrown:
scala> m('x).x = 7
<console>:10: error: ')' expected but string literal found.
+ "m(scala.Symbol("x")).x: Int = " + `$ires0` + "\n"
^
I created a ticket for this.

Related

Why does this code do work: "map.apply(1)+=3"

val map = scala.collection.mutable.Map(1 -> 2)
map(1) += 3
map.apply(1) += 3
(map.apply(1)).+=(3)
I don't understand why the codes are all compiling fine.
In the first case, I think the code is expanded to map(1) = map(1) + 3, and to map.update(1, map(1) + 3).
But in the second and third cases,
map.apply(1) = map.apply(1) + 3 causes a compilation error, of cause.
How are the second and third code expanded to?
Running :replay -Xprint:typer from the scala console:
1) map(1) += 3 expands to:
map.update(1, map.apply(1).+(3))
2) map.apply(1) += 3 expands to:
map.update(1, map.apply(1).+(3))
3) (map.apply(1)).+=(3) expands to:
map.update(1, map.apply(1).+(3))
EDIT Answer to the question in the comments
If all three expansions are the same, why second and third causes a compilation error?
The second and third: map.apply(1) += 3 and (map.apply(1)).+=(3) are compiling fine and are also equivalent.
What I tried to prove with my answer is that: map.apply(1) += 3 doesn't expand to map.apply(1) = map.apply(1) + 3 as explained by #som-snytt in the first part of his answer.
BTW map(1) = map(1) + 3 does not expands to map.update(1, map(1) + 3) as stated in the question.
I hope this clarify my answer.
The rule for update is in the spec under assignments, and expansion of assignment operators here.
The question is why is explicit m.apply not taken as m() for purposes of the update rule.
The two forms are supposed to be equivalent.
Someone just debated update syntax with examples.
scala> import reflect.runtime.universe._
import reflect.runtime.universe._
scala> val map = scala.collection.mutable.Map(1 -> 2)
map: scala.collection.mutable.Map[Int,Int] = Map(1 -> 2)
scala> reify(map(1) += 3)
res0: reflect.runtime.universe.Expr[Unit] = Expr[Unit]($read.map.update(1, $read.map.apply(1).$plus(3)))
scala> reify(map.apply(1) += 3)
res1: reflect.runtime.universe.Expr[Unit] = Expr[Unit]($read.map.update(1, $read.map.apply(1).$plus(3)))
scala> reify(map(1) = map(1) + 3)
res2: reflect.runtime.universe.Expr[Unit] = Expr[Unit]($read.map.update(1, $read.map.apply(1).$plus(3)))
scala> reify(map.apply(1) = map.apply(1) + 3)
<console>:16: error: missing argument list for method apply in trait MapLike
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `apply _` or `apply(_)` instead of `apply`.
reify(map.apply(1) = map.apply(1) + 3)
^
scala> map.apply.update(1, map.apply(1) + 3)
<console>:16: error: missing argument list for method apply in trait MapLike
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `apply _` or `apply(_)` instead of `apply`.
map.apply.update(1, map.apply(1) + 3)
^
Edit: FWIW, that's just how it is.
Edit:
This is the anomaly:
scala> val m = collection.mutable.Map(1->2)
m: scala.collection.mutable.Map[Int,Int] = Map(1 -> 2)
scala> m(1) = m(1) + 3
scala> m(1) += 3
scala> m.apply(1) += 3
scala> m.apply(1) = m.apply(1) + 3
<console>:13: error: missing argument list for method apply in trait MapLike
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `apply _` or `apply(_)` instead of `apply`.
m.apply(1) = m.apply(1) + 3
^
Since these expressions are all equivalent, they should all compile to an invocation of update.
The last expression fails to typecheck because the compiler does a mechanical rewrite to m.apply.update(1, m.apply(1) + 3) instead of m.update.
The explanation in gitter chat is that, well, the compiler isn't required to be smart enough to recognize m.apply(1) as m(1) in this context. After all, possibly ambiguities ensue. What if apply is parameterless and returns a value with an update method? Do you take m.apply(1) as m(1) only if it doesn't typecheck otherwise?
It's clear that, by the spec, m(1) += ??? is expanded to m(1) = m(1) + ??? and then converted to m.update(1, m(1) + ???).
In the code, the two transformations (converting op= to x = x op expr and x(1) = ??? to x.update(1, ???)) are compressed:
Deciding if something is mutable
On error with op=, attempt conversion to assignment
Converting to update (or to plain assignment).
It might be possible to work around the limitation in the implementation, but it's not obvious that it would spec nicely (as above, where apply might be paramless).
Then should m.apply(1) += 3 fail to compile, for symmetry? If the compiler worked harder to retain the source expression, it could at least be more consistent in this case.
FWIW, this works in 2.12.0-M3
C:\Users\erichardson>scala
Welcome to Scala 2.12.0-M3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_66).
Type in expressions for evaluation. Or try :help.
scala> val map = scala.collection.mutable.Map(1 -> 2)
map: scala.collection.mutable.Map[Int,Int] = Map(1 -> 2)
scala> map(1) += 3
scala> map
res1: scala.collection.mutable.Map[Int,Int] = Map(1 -> 5)
scala> map.apply(1) += 3
scala> map
res3: scala.collection.mutable.Map[Int,Int] = Map(1 -> 8)
scala> (map.apply(1)).+=(3)
scala> map
res5: scala.collection.mutable.Map[Int,Int] = Map(1 -> 11)
scala>

Scala inferring wrong type when using Seq append operator "+:"

I'm still pretty new to Scala. I'm having trouble trying to append two Sequences together because the compiler is complaining about the type of the Seq. I would like to start with a Seq[String] var and replace it with the addition of two Seq[String]'s. In the REPL session below, we see that y :+ x is a Seq[Object], but why?
Welcome to Scala version 2.11.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_71).
Type in expressions to have them evaluated.
Type :help for more information.
scala> val x = Seq[String]("a")
x: Seq[String] = List(a)
scala> var y = Seq[String]("b")
y: Seq[String] = List(b)
scala> y = y :+ x
<console>:9: error: type mismatch;
found : Seq[Object]
required: Seq[String]
y = y :+ x
^
scala> val z = y :+ x
z: Seq[Object] = List(b, List(a))
It's because the :+ operator expects a single item, not a sequence. So what you're trying to do is comparable to var y:List[String] = List("b", List("a")), which isn't valid. You can see this in the documentation of Seq, which shows the type of :+ to be A => Seq[A].
I think you probably want to use the ++ operator instead.

Scala applying implicit functions to a collection

EDIT: I'm using Scala 2.9.2
In Scala, I've defined a custom class which wraps a Double:
class DoubleWrap( d : Double ) {
def double( ) = d * 2
}
and an implicit conversion from Double to DoubleWrap:
implicit def wrapDouble( d : Double ) = new DoubleWrap( d )
This allows me to do the following:
scala> 2.5.double
res0: Double = 5.0
However, since there is an implicit conversion in Scala from Int to Double, I can also do the following:
scala> 2.double
res1: Double = 4.0
This operator can also be applied to all elements of a double-type collection using map
scala> List( 1.0, 2.0, 3.0 ).map( _.double )
res2: List[Double] = List(2.0, 4.0, 6.0)
However, if I attempt to apply the function to all elements of an integer collection, it doesn't work:
scala> List( 1, 2, 3 ).map( _.double )
<console>:10: error: value double is not a member of Int
List( 1, 2, 3 ).map( _.double )
Does anyone know why this is the case?
In scala, implicit conversions are not automatically chained. In other words, the compiler will look for a single implicit conversion that will allow the code to make sense, it will never try to apply two (or more) successive implicit conversions.
In your example, the fact that you can do 2.double has nothing to do with the fact that there is an implicit conversion from Double to Int in Predef.
As a proof, try this in the REPL:
scala> val i: Int = 2
i: Int = 2
scala> i.double
<console>:13: error: value double is not a member of Int
i.double
It does not compile.
So why does 2.double compile? Good question.
I thought I understood this intuitively: 2 can be interpreted as the Int value 2 or as the Double value 2.0 in the first place, so my intuition was that 2 is somehow already a Double in this context.
However, I think this is wrong, because even the following will compile, surpisingly: (2:Int).double (or even more strange: ((1+1):Int).double). I'll be honest, I am flabbergasted and have no idea why this compiles while val i: Int = 2; i.double does not.
So to sum up, it is normal that scala does not try to apply two implicit conversions at the same time, but for some reason this rule does not seem to apply to constant expressions.
And now for a way to fix your issue: simply modify your implicit conversion so that it accepts any type that is itself implicitly convertible to Double. In effect, this allows to chain the implicit conversions:
implicit def wrapDouble[T <% Double]( d : T ) = new DoubleWrap( d )
It's a bug which should soon be fixed.

Unit as parameter

What is the following methods' difference?
def sum1() = 1+2
def sum2(a:Unit) = 1+2
I think they are semantically identical, is it right?
With sum1, you can call it with or without parentheses:
val x = sum1 // x: Int = 3
val y = sum1() // y: Int = 3
But with sum2 you are forced to provide parentheses.. I think that if you call sum2(), you are actually calling sum2 with () as the argument a.
val x2 = sum2 // error
val y2 = sum2() // y2: Int = 3
Note that passing unit as an argument to an expression lets you simulate lazy evaluation in a strict language. By "moving evaluation under a lambda" you ensure that the expression isn't eval'd until the () gets passed in. This can be useful for e.g. auto-memoizing data structures, which collapse from a function to a value the first time they're inspected.
These methods are not identical. Once receives a parameter, the other does not. See here:
scala> sum1(println("Hi, there!"))
<console>:9: error: too many arguments for method sum1: ()Int
sum1(println("Hi, there!"))
^
scala> sum2(println("Hi, there!"))
Hi, there!
res1: Int = 3

Scala Tuple Deconstruction

I am new to Scala, and ran across a small hiccup that has been annoying me.
Initializing two vars in parallel works great: var (x,y) = (1,2)
However I can't find a way to assign new values in parallel: (x,y) = (x+y,y-x) //invalid syntax
I end up writing something like this: val xtmp = x+y; y = x-y; x = xtmp
I realize writing functional code is one way of avoiding this, but there are certain situations where vars just make more sense.
I have two questions:
1) Is there a better way of doing this? Am I missing something?
2) What is the reason for not allowing true parallel assignment?
Unfortunately, you cannot do multiple assignments in Scala. But you may use tuples, if they fit your problem:
scala> var xy = (1,2)
xy: (Int, Int) = (1,2)
scala> xy = (xy._1 + xy._2, xy._2 - xy._1)
xy: (Int, Int) = (3,1)
This way, xy is one tuple with two values. The first value can be accessed using xy._1, the second one using xy._2.
Scala has 2 types of variables: vals and vars. Vals are similar to Java's final variables, so as far as I understand from what you're asking, the only way to assign new values in parallel to vals is by:
scala> val (x, y) = (1, 2);
or
scala> val s = (3, 4);
s: (Int, Int) = (3,4)
scala> s._1
res1: Int = 3
scala> s._2
res2: Int = 4