更新时间:2022-10-15 15:18:59
完全一样,感谢Predef
中的这个类(这里只复制了一部分):
final class ArrowAssoc[A](val __leftOfArrow: A) extends AnyVal {@inline def ->[B](y: B): Tuple2[A, B] = Tuple2(__leftOfArrow, y)}@inline 隐式 def any2ArrowAssoc[A](x: A): ArrowAssoc[A] = new ArrowAssoc(x)
那么现在的问题是 (a,b)
语法何时会模糊,而 (a -> b)
不会?答案就在函数调用中,尤其是当它们被重载时:
def f[A](a: A) = a.toStringdef f[A,B](a: A, b: B) = a.hashCode + b.hashCodef(1,2)//整数 = 3f(1 -> 2)//字符串 = (1,2)f((1, 2))//字符串 = (1,2)
Map +
特别容易混淆,因为它被多参数版本重载,所以你可以
Map(1 -> 2) + (3 -> 4, 4 -> 5, 5 -> 6)
它因此解释
Map(1 -> 2) + (3, 4)
尝试将 3
添加到地图,然后将 4
添加到地图.这当然没有意义,但它没有尝试其他解释.
使用 ->
就没有这样的歧义.
但是,你不能
Map(1 -> 2) + 3 ->4
因为 +
和 -
具有相同的优先级.因此它被解释为
(Map(1 -> 2) + 3) ->4
再次失败,因为您尝试添加 3
代替键值对.
I am getting a taste of Scala through the artima "Programming in Scala" book.
While presenting the Map
traits, the authors go to some lengths to describe the ->
syntax as a method that can be applied to any type to get a tuple.
And indeed:
scala> (2->"two")
res1: (Int, String) = (2,two)
scala> (2,"two")
res2: (Int, String) = (2,two)
scala> (2->"two") == (2, "two")
res3: Boolean = true
But those are not equivalent:
scala> Map(1->"one") + (2->"two")
res4: scala.collection.immutable.Map[Int,String] = Map(1 -> one, 2 -> two)
scala> Map(1->"one") + (2, "two")
<console>:8: error: type mismatch;
found : Int(2)
required: (Int, ?)
Map(1->"one") + (2, "two")
Why is this so, since my first tests seem to show that both "pair" syntaxes build a tuple?
Regards.
They are exactly the same, thanks to this class in Predef
(only partly reproduced here):
final class ArrowAssoc[A](val __leftOfArrow: A) extends AnyVal {
@inline def -> [B](y: B): Tuple2[A, B] = Tuple2(__leftOfArrow, y)
}
@inline implicit def any2ArrowAssoc[A](x: A): ArrowAssoc[A] = new ArrowAssoc(x)
So now the question is when will (a,b)
syntax be ambiguous where (a -> b)
is not? And the answer is in function calls, especially when they're overloaded:
def f[A](a: A) = a.toString
def f[A,B](a: A, b: B) = a.hashCode + b.hashCode
f(1,2) // Int = 3
f(1 -> 2) // String = (1,2)
f((1, 2)) // String = (1,2)
Map +
in particular gets confused because it's overloaded with a multiple-argument version, so you could
Map(1 -> 2) + (3 -> 4, 4 -> 5, 5 -> 6)
and it thus interprets
Map(1 -> 2) + (3, 4)
as trying to add both 3
to the map, and then 4
to the map. Which of course makes no sense, but it doesn't try the other interpretation.
With ->
there is no such ambiguity.
However, you can't
Map(1 -> 2) + 3 -> 4
because +
and -
have the same precedence. Thus it is interpreted as
(Map(1 -> 2) + 3) -> 4
which again fails because you're trying to add 3
in place of a key-value pair.