为什么运营商有差异?
为什么运营商有差异?
我长期以来一直在关注,这是我第一次发帖。
在Scala中,我正在寻找优势,看看为什么喜欢根据类型改变操作符。例如,为什么这样做:
Vector(1, 2, 3) :+ 4
被确定为优势,而不是:
Vector(1, 2, 3) + 4
或:
4 +: Vector(1,2,3)
而不是:
Vector(4) + Vector(1,2,3)
或者:
Vector(1,2,3) ++ Vector(4,5,6)
而不是:
Vector(1,2,3) + Vector(4,5,6)
所以,我们有:+、+:和++,当仅使用+就足以满足时。我是Scala的新手,我会接受。但是,这似乎是一个不必要的、混淆的语言,试图在语法上保持清晰。
我做了很多谷歌和堆栈溢出搜索,只找到了有关特定运算符和运算符重载的问题。但是,没有关于为什么必须将+等拆分为多个变体的背景。
附言,我可以使用隐式类重载运算符,如下面所示,但我想这只会引起经验丰富的Scala程序员使用/阅读我的代码时的困惑和不满。
object AddVectorDemo { implicit class AddVector(vector : Vector[Any]) { def +(that : Vector[Any]) = vector ++ that def +(that : Any) = vector :+ that } def main(args : Array[String]) : Unit = { val u = Vector(1,2,3) val v = Vector(4,5,6) println(u + v) println(u + v + 7) } }
输出:
Vector(1, 2, 3, 4, 5, 6)
Vector(1, 2, 3, 4, 5, 6, 7)
这是一个很公平的问题,我认为和遗留代码和Java兼容性有很大关系。Scala复制了Java的字符串连接的+
,这使得事情变得更加复杂。这个+
可以让我们做到:
(new Object) + "foobar" //"java.lang.Object@5bb90b89foobar"
。那么如果我们用+
来连接List
,并且执行List(1) + "foobar"
,我们能得到什么呢?有人可能会期望得到List(1, "foobar")
(类型为List[Any]
),就像我们用:+
时得到的一样,但Java风格的字符串重载会使这个问题变得复杂,因为编译器无法解析重载。Odersky曾经评论过:
在元素类型协变的集合中,绝不能有
+
方法。集合和映射都是非协变的,所以它们可以有+
方法。这一切都相当微妙和混乱。如果我们不试图复制Java的字符串连接+
,我们会更好。但是当Scala被设计时,想法是尽可能保留Java的所有表达式语法,包括字符串+
。现在改变已经太迟了。
有一些讨论(虽然是在不同的上下文中)在这个类似的问题的回答中。
答案需要一个令人惊讶的长方差旅程。我会尽可能地简短说明。
首先,请注意您可以向现有的Vector添加任何内容:
scala> Vector(1) res0: scala.collection.immutable.Vector[Int] = Vector(1) scala> res0 :+ "fish" res1: scala.collection.immutable.Vector[Any] = Vector(1, fish)
为什么可以这样做?如果B extends A
,并且我们想要能够使用Vector[A]
要求Vector[B]
,我们需要允许Vector[B]
添加与Vector[A]
可以添加相同类型的内容。但是,一切都延伸自Any
,所以我们需要允许添加Vector[Any]
可以添加的任何内容,即所有内容。
使Vector
和大多数其他非Set集合具有协变性是一个设计决策,但这是大多数人期望的。
现在,让我们尝试将向量添加到向量。
scala> res0 :+ Vector("fish") res2: scala.collection.immutable.Vector[Any] = Vector(1, Vector(fish)) scala> res0 ++ Vector("fish") res3: scala.collection.immutable.Vector[Any] = Vector(1, fish)
如果我们只有一个操作+
,我们将无法指定我们要添加的哪个内容。而且我们可能真的想做其中任何一种。这两种尝试都是非常合理的。我们可以根据类型猜测,但实际上最好是要求程序员明确表示他们的意思。由于有两种不同的意思,因此需要有两种不同的询问方式。
这在实践中出现过吗?使用集合的集合,是的,一直如此。例如,使用+
:
scala> Vector(Vector(1), Vector(2)) res4: Vector[Vector[Int]] = Vector(Vector(1), Vector(2)) scala> res4 + Vector(3) res5: Vector[Any] = Vector(Vector(1), Vector(2), 3)
那可能不是我想要的。