为什么运营商的变化?

l p*_*l p 3 scala operators

长时间潜伏,第一次海报.

在Scala中,我正在寻找优势,以便根据类型改变运算符.例如,为什么这样:

Vector(1, 2, 3) :+ 4
Run Code Online (Sandbox Code Playgroud)

决心成为优势:

Vector(1, 2, 3) + 4
Run Code Online (Sandbox Code Playgroud)


要么:

4 +: Vector(1,2,3)
Run Code Online (Sandbox Code Playgroud)

过度:

Vector(4) + Vector(1,2,3)
Run Code Online (Sandbox Code Playgroud)


要么:

Vector(1,2,3) ++ Vector(4,5,6)
Run Code Online (Sandbox Code Playgroud)

过度:

Vector(1,2,3) + Vector(4,5,6)
Run Code Online (Sandbox Code Playgroud)


所以,这里我们有:+,+:和++当+单独就足够了.我是斯卡拉的新人,我会屈服.但是,对于一种试图用它的语法来清理的语言来说,这似乎是不必要的和混淆的.

我已经做了很多google和堆栈溢出搜索,并且只发现了有关特定运算符和运算符重载的问题.但是,没有背景为什么有必要将+分成例如多个变体.

FWIW,我可以使用隐式类重载运算符,如下所示,但我想这只会导致经验丰富的Scala程序员使用/读取我的代码时产生混淆(和tisk风险).

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

矢量(1,2,3,4,5,6)
矢量(1,2,3,4,5,6,7)

Rex*_*err 6

答案需要通过差异进行令人惊讶的长途跋涉.我会尽量让它尽可能短.

首先,请注意您可以向现有Vector添加任何内容:

scala> Vector(1)
res0: scala.collection.immutable.Vector[Int] = Vector(1)

scala> res0 :+ "fish"
res1: scala.collection.immutable.Vector[Any] = Vector(1, fish)
Run Code Online (Sandbox Code Playgroud)

你为什么这样做?好吧,如果B extends A我们希望能够使用所需的Vector[B]地方Vector[A],我们需要允许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)
Run Code Online (Sandbox Code Playgroud)

如果我们只有一个操作,+我们将无法指定我们所指的这些东西中的哪一个.而且我们真的可能会这样做.他们都是非常明智的尝试.我们可以尝试根据类型进行猜测,但实际上最好只要求程序员明确说出它们的含义.由于有两个不同的含义,需要有两种方式可以提出.

这是否在实践中出现?随着收藏集,是的,所有的时间.例如,使用您的+:

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)
Run Code Online (Sandbox Code Playgroud)

那可能不是我想要的.


Ben*_*ich 5

这是一个公平的问题,我认为它与遗留代码和Java兼容性有很大关系.Scala复制了Java +用于字符串连接,这使事情变得复杂.

+允许我们这样做:

(new Object) + "foobar" //"java.lang.Object@5bb90b89foobar"
Run Code Online (Sandbox Code Playgroud)

所以,我们应该想到,如果我们有+List我们做了List(1) + "foobar"?人们可能期望List(1, "foobar")(类型List[Any]),就像我们使用的那样:+,但是Java启发的String-concatenation重载会使这变得复杂,因为编译器将无法解决重载.

奥德斯基甚至曾评论过:

对于在其元素类型中具有协变性的集合,应该永远不会有+方法.集合和映射是非变量的,这就是为什么它们可以有+方法.这一切都相当精致和凌乱.如果我们不尝试复制Java的+用于字符串连接,我们会更好.但是当Scala得到设计时,我们的想法就是保留Java的所有表达式语法,包括String +.现在改变它已经太晚了.

关于这个类似问题的答案,有一些讨论(尽管在不同的背景下).