为什么运营商会有所不同?
Why the variation in 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)
所以,这里我们有 :+、+: 和 ++,而单独使用 + 就足够了。我是 Scala 的新手,我会屈服的。但是,这 似乎 是不必要的,并且对于一种试图用其语法保持干净的语言来说是混淆的。
我做了很多 google 和堆栈溢出搜索,只发现了关于特定运算符和一般运算符重载的问题。但是,没有关于为什么有必要将 + 拆分为多个变体的背景。
FWIW,我可以使用隐式 类 重载运算符,如下所示,但我认为这只会导致经验丰富的 Scala 程序员 using/reading 我的代码感到困惑(和 tisk tisks)。
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)
答案需要通过差异绕出令人惊讶的漫长弯路。我会尽量缩短。
首先,请注意您可以向现有 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
和大多数其他 non-Set collections 协变是一个设计决策,但这是大多数人所期望的。
现在,让我们尝试将一个向量添加到一个向量中。
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)
如果我们只有一个操作,+
,我们将无法具体说明我们指的是哪一个。我们真的 可能 打算做任何一个。它们都是非常明智的尝试。我们可以尝试根据类型进行猜测,但实际上最好只要求程序员明确说明它们的含义。由于有两种不同的意思,因此需要有两种询问方式。
这在实践中出现了吗?使用 collections of collections,是的,一直都是。例如,使用您的 +
:
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)
这可能不是我想要的。
这是一个公平的问题,我认为它与遗留代码和 Java 兼容性有很大关系。 Scala 复制了 Java 的 +
用于字符串连接,这有一些复杂的事情。
这 +
让我们可以做到:
(new Object) + "foobar" //"java.lang.Object@5bb90b89foobar"
那么,如果 List
有 +
而我们有 List(1) + "foobar"
,我们应该期待什么?人们可能会期望 List(1, "foobar")
(List[Any]
类型),就像我们使用 :+
时得到的一样,但是 Java 启发的字符串连接重载会使这个复杂化,因为编译器将无法解析重载。
奥德斯基甚至曾经评论过:
One should never have a + method on collections that are covariant in their element type. Sets and maps are non-variant, that's why they can have a + method. It's all rather delicate and messy. We'd be better off if we did not try to duplicate Java's + for String concatenation. But when Scala got designed the idea was to keep essentially all of Java's expression syntax, including String +. And it's too late to change that now.
关于 this similar question 的答案有一些讨论(尽管在不同的上下文中)。
潜伏已久,第一次发布。
在 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 的新手,我会屈服的。但是,这 似乎 是不必要的,并且对于一种试图用其语法保持干净的语言来说是混淆的。
我做了很多 google 和堆栈溢出搜索,只发现了关于特定运算符和一般运算符重载的问题。但是,没有关于为什么有必要将 + 拆分为多个变体的背景。
FWIW,我可以使用隐式 类 重载运算符,如下所示,但我认为这只会导致经验丰富的 Scala 程序员 using/reading 我的代码感到困惑(和 tisk tisks)。
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)
答案需要通过差异绕出令人惊讶的漫长弯路。我会尽量缩短。
首先,请注意您可以向现有 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
和大多数其他 non-Set collections 协变是一个设计决策,但这是大多数人所期望的。
现在,让我们尝试将一个向量添加到一个向量中。
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)
如果我们只有一个操作,+
,我们将无法具体说明我们指的是哪一个。我们真的 可能 打算做任何一个。它们都是非常明智的尝试。我们可以尝试根据类型进行猜测,但实际上最好只要求程序员明确说明它们的含义。由于有两种不同的意思,因此需要有两种询问方式。
这在实践中出现了吗?使用 collections of collections,是的,一直都是。例如,使用您的 +
:
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)
这可能不是我想要的。
这是一个公平的问题,我认为它与遗留代码和 Java 兼容性有很大关系。 Scala 复制了 Java 的 +
用于字符串连接,这有一些复杂的事情。
这 +
让我们可以做到:
(new Object) + "foobar" //"java.lang.Object@5bb90b89foobar"
那么,如果 List
有 +
而我们有 List(1) + "foobar"
,我们应该期待什么?人们可能会期望 List(1, "foobar")
(List[Any]
类型),就像我们使用 :+
时得到的一样,但是 Java 启发的字符串连接重载会使这个复杂化,因为编译器将无法解析重载。
奥德斯基甚至曾经评论过:
One should never have a + method on collections that are covariant in their element type. Sets and maps are non-variant, that's why they can have a + method. It's all rather delicate and messy. We'd be better off if we did not try to duplicate Java's + for String concatenation. But when Scala got designed the idea was to keep essentially all of Java's expression syntax, including String +. And it's too late to change that now.
关于 this similar question 的答案有一些讨论(尽管在不同的上下文中)。