为什么Scala支持具有针对每个定义的表达式的多名称定义?

Geo*_*ins 3 scala

对于大多数语言,我不会问"他们为什么这样做X?" 因为它通常基于意见.

对于像C++这样的语言,通常的答案是引用一些规范,并且所有人都可以说,在做出特定选择之前,有人可能认为很长很难(但可能做出了不同的选择).

Scala是不同的 - 通常答案是指出你可以用一些更简单的结构Y和Z来思考X,因此在这种情况下看X的表达是有意义的.

因此,我会问为什么Scala允许定义引入多个名称并为每个名称计算一次给定的表达式?

如果你让Java程序员猜测这里会发生什么,他们可能会猜错:

var x, y, z = 3
Run Code Online (Sandbox Code Playgroud)

也就是说,他们猜测只会z分配一个值.

如果你解释val然后告诉他们以下是合法的:

val x, y, z = 3
Run Code Online (Sandbox Code Playgroud)

然后他们可能会猜测更多是正在进行的,x并且y必须在此行之后有值,因为以后不能为它们分配不同的值.

他们可能会假设x并y为其类型采用默认值,例如0表示整数,但因为这里没有明确的类型,这是一个飞跃.

他们可能会认为它被处理为:

val z = 3
val x = z
val y = z
Run Code Online (Sandbox Code Playgroud)

当左边的表达式=产生原始值或不可变对象时,这并不重要.但以下可能会让他们怀疑:

val x, y, z = new StringBuilder
Run Code Online (Sandbox Code Playgroud)

为什么有人想为同一个StringBuilder实例引入三个名字?

如果你向他们展示了以下内容,他们可能会从构造中猜出,甚至在运行代码之前出现了奇怪的事情:

var i = 0

def f: Int = {
  i += 1
  println(i)
  i
}

val x, y, z = f
Run Code Online (Sandbox Code Playgroud)

一个人最终意识到,只显示与之相关的表达式z实际上是为每个名称评估一次,即上述内容相当于:

val x = f
val y = f
val z = f
Run Code Online (Sandbox Code Playgroud)

那么谈论习惯于另一种语言的程序员可能会想到什么呢?

大多数人从其他地方来到像Scala这样的语言,所以除非有充分的理由,否则应该避免可能混淆的程度结构.

乍一看这个功能似乎并没有提供太多,它避免了你不得不重复自己,但为这么小的收益添加这段相当混乱的语法糖似乎很奇怪.

那么在某种情况下它会带来真正的好处吗?或者这里没有真正的收获,但是我们与其他地方建立的更广泛的模式保持某种逻辑一致性?

sjr*_*jrd 5

有一种情况是使用这个令人惊讶的功能:定义时scala.Enumeration:

object Weekday extends scala.Enumeration {
  val Monday, Tuesday, Wednesday, Thursday, Friday = Value
}
Run Code Online (Sandbox Code Playgroud)

其中5次调用Value(def继承自Enumeration),枚举的每个字段调用一次.实际上,这Enumeration#Value为每个字段分配了一个新实例,这显然是枚举有用的必要条件.

如果没有这个多任务功能,您必须写:

object Weekday extends scala.Enumeration {
  val Monday = Value
  val Tuesday = Value
  val Wednesday = Value
  val Thursday = Value
  val Friday = Value
}
Run Code Online (Sandbox Code Playgroud)

我从未见过在其他任何地方使用的多任务功能,但在Enumeration声明中.

在语言设计方面这是否是一个好主意是一个主观问题,而且SO不是讨论它的正确位置.