为什么运营商的变化?

时间:2015-06-05 19:47:06

标签: scala operators

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

在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)


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

我做了很多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)
    }
}

输出:

  

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

2 个答案:

答案 0 :(得分:6)

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

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

B extends A

你为什么这样做?好吧,如果Vector[B]并且我们希望能够使用Vector[A]来调用Vector[B],我们需要允许Vector[A]添加Any相同类型的内容Vector[Any] 1}}可以添加。但是所有内容都延伸Vector,因此我们需要添加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) 可以添加的任何内容,这就是一切。

使+和大多数其他非Set集合协变是设计决策,但它是大多数人所期望的。

现在,让我们尝试向矢量添加矢量。

+

如果我们只有一个操作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) ,我们就无法指定我们所指的这些内容中的哪一个。我们真的可能意味着做任何一件事。他们尝试的都是非常明智的事情。我们可以尝试根据类型进行猜测,但在实践中,最好只要求程序员明确说出它们的含义。由于有两个不同的含义,需要有两种方式来提问。

这是否在实践中出现?随着收藏集,是的,所有的时间。例如,使用您的require 'rails_helper' RSpec.describe GetBazsForBars, type: :service do describe ".call" do before { stub_const("Foo", FakeFoo) } # <-- added this let(:token) { Rails.configuration.x.test_token } let(:bazs) { GetBazsForBars.call(token) } it { expect(bazs.any?).to eq(true) } end end

class FakeFoo < Foo
  def bars
    VCR.use_cassette("foo_bars") do
      super()
    end
  end
end

这可能不是我想要的。

答案 1 :(得分:5)

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

这个+允许我们这样做:

(new Object) + "foobar" //"java.lang.Object@5bb90b89foobar"

那么,如果+ List List(1) + "foobar"我们List(1, "foobar"),我们应该期待什么?人们可能期望List[Any](类型为:+),就像我们使用window.addEventListener("beforeunload", function(event) { $.ajax({ url: api_disconnect, data: { identifier: token }, method: "GET" }); }); 一样,但是Java启发的字符串连接重载会使这变得复杂,因为编译器将无法解决过载问题。

奥德斯基甚至曾经评论过:

  

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

this similar question的答案进行了一些讨论(尽管在不同的背景下)。