参数解构

Argument destructuring

我有 JS 背景。在 JS 中我可以这样写:

let x = [[1, 0], [2, 1], [3, 2], [4, 3]]
x.forEach(([n, i]) => console.log())

所以我尝试转换为 Scala,并且找到了很多方法。但我不明白 matchcase 语句是如何消失的。

val x = Array(1, 2, 3, 4).zipWithIndex

// does what I expect
x.foreach((a) => {
    println(a)
})

// uses match
x.foreach((a) => {
    a match {
        case (n, i) => println(s"$n $i")
    }
})

// gets rid of redundant variable name
x.foreach({
    _ match {
        case (n, i) => println(s"$n $i")
    }
})

// gets rid of unnecesary scope
x.foreach(_ match {
    case (n, i) => println(s"$n $i")
})

到这里为止,它是有道理的。以下代码是我在查看如何使用索引循环时在网上找到的。

// where did `match` go?
x.foreach({
    case (n, i) => println(s"$n $i")
})

// and now `case` is gone too?
x.foreach((n, i) => println(s"$n $i"))

这是怎么回事?我会称它为来自 JS 的解构,但是这个 似乎 就像一个 hidden/implicit match/case 语句。有相关规则吗?我怎么知道是否应该有隐含的 match/case 语句?

关于 match 关键字:

// where did `match` go?
x.foreach({
    case (n, i) => println(s"$n $i")
})

这是语言规范,可以忽略匿名函数中的匹配关键字:https://scala-lang.org/files/archive/spec/2.13/08-pattern-matching.html#pattern-matching-anonymous-functions

// where did `match` go?
x.foreach({
    case (n, i) => println(s"$n $i")
})

这是一个模式匹配匿名函数,有时也称为部分函数文字。有关所有详细信息,请参阅 Scala Language Specification 8.5 Pattern Matching Anonymous Functions。简单地说,表达式

{
  case p1 => e1
  case p2 => e2
  // …
  case pn => en
}

等同于

(x1: S1, x2: S2, /* … */, xn: Sn) => (x1, x2, /* … */, xn) match {
  case p1 => e1
  case p2 => e2
  // …
  case pn => en
}

前提是结果类型可以 SAM 转换为 FunctionN[S1, S2, /* … */, Sn, R],或者作为特殊情况 PartialFunction1[S1, R](名称 部分函数文字来自。)

// and now `case` is gone too?
x.foreach((n, i) => println(s"$n $i"))

这是 Scala 3 的新特性。很长一段时间以来,Scala 的设计者都想统一 元组参数列表 .换句话说,他们想让 Scala 中的方法只接受一个参数,而这个参数就是一个元组。不幸的是,事实证明 a) 这极大地破坏了向后兼容性和 b) 极大地破坏了平台互操作性。

现在,Scala 3 是一个忽略问题 a) 的机会,但你不能忽略问题 b),因为 Scala 的主要设计目标之一是与底层主机平台无缝、紧密、良好、高效地集成(例如 .NET 在现在被遗弃的 Scala.NET 的情况下,ECMASCript / HTML5 / DOM / WebAPI 平台在 Scala.js 的情况下,本机操作系统在 Scala-native 的情况下,或者 Java 平台(JRE、JDK、JVM、J2SE、J2EE、Java、Kotlin、Clojure 等)在 Scala-JVM 的情况下).

然而,Scala 的设计者设法找到了折衷方案,参数和元组不是一回事,但是参数可以很容易地转换为元组,元组也可以很容易地转换为参数。

这个叫做Parameter Untupling,基本意思是FunctionN[S1, S2, /* … */, Sn, R]类型的函数可以自动转换为Function1[(S1, S2, /* … */, Sn), R]类型的函数,是[=20]的语法糖=].

简单地说,

(p1: S1, p2: S2, /* … */, pn: Sn) => e: R

可以自动转换为

(x: (S1, S2, /* … */, Sn)) => {
  val p1: S1 = x._1
  val p2: S2 = x._2
  // …
  val pn: Sn = x._n

  e
}

注意:不幸的是,目前还没有完整的 Scala 3 规范。有部分 Language Reference,但它仅描述了与 Scala 2 的差异。因此,您通常必须在 SLS 和 Scala 3 文档之间来回切换。