为什么此代码有效:"map.apply(1)+=3"

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)

我不明白为什么代码都可以编译好。

在第一种情况下,我认为代码扩展为map(1) = map(1) + 3,然后扩展为map.update(1, map(1) + 3)。 但是在第二种和第三种情况下, map.apply(1) = map.apply(1) + 3 导致编译错误,原因。

第二个和第三个代码是如何展开的?

运行 :replay -Xprint:typer 来自 Scala 控制台:

1) map(1) += 3 扩展为:

map.update(1, map.apply(1).+(3))

2) map.apply(1) += 3 扩展为:

map.update(1, map.apply(1).+(3))

3) (map.apply(1)).+=(3) 扩展为:

map.update(1, map.apply(1).+(3))

编辑评论中问题的答案

If all three expansions are the same, why second and third causes a compilation error?

第二个和第三个:map.apply(1) += 3(map.apply(1)).+=(3) 编译得很好,也是等价的。

我试图用我的答案来证明的是:map.apply(1) += 3 不会扩展到 map.apply(1) = map.apply(1) + 3 正如 @som-snytt 在第一部分中所解释的那样他的回答。

顺便说一句 map(1) = map(1) + 3 不会 扩展到 map.update(1, map(1) + 3) 如问题中所述。

我希望这能澄清我的回答。

更新规则为in the spec under assignments, and expansion of assignment operators here

问题是为什么明确的 m.apply 不被视为 m() 用于更新规则。

两种形式应该是equivalent.

刚刚有人 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)
           ^

编辑:FWIW,that's just how it is

编辑:

这是异常:

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
         ^

由于这些表达式都是等价的,它们都应该编译为 update.

的调用

最后一个表达式无法进行类型检查,因为编译器将机械重写为 m.apply.update(1, m.apply(1) + 3) 而不是 m.update

gitter chat 中的解释是,在这种情况下,编译器不需要足够聪明来将 m.apply(1) 识别为 m(1)。毕竟,可能会出现歧义。如果 apply 是无参数的并且 returns 是具有 update 方法的值怎么办?只有在不进行类型检查的情况下,您才会将 m.apply(1) 视为 m(1) 吗?

很明显,根据规范,m(1) += ??? 扩展为 m(1) = m(1) + ???,然后转换为 m.update(1, m(1) + ???)

在代码中,压缩了两个转换(将op=转换为x = x op expr和将x(1) = ???转换为x.update(1, ???)):

Deciding if something is mutable

On error with op=, attempt conversion to assignment

Converting to update(或简单赋值)。

可能可以解决实施中的限制,但它是否会很好地规范并不明显(如上所述,其中 apply 可能是无参数的)。

那么 m.apply(1) += 3 是否应该编译失败,为了对称?如果编译器更加努力地保留源表达式,它至少可以在这种情况下更加一致。

FWIW,这适用于 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>