1

trait Expr
case class Number(n: Int) extends Expr
case class Sum(e1: Expr, e2: Expr) extends Expr

object CaseExample {
  def eval(e: Expr): Int = e match {
    case Number(n) => n
    case Sum(e1, e2) => eval(e1) + eval(e2)
  }
  def main(args: Array[String]) {
    println(eval(Sum(Number(1), Number(2))))       //> 3
  }
}

有相当多的语法糖正在发生。我知道这case是隐式创建两个对象

object Number extends Expr {
  def apply(n: Int) = new Number(n)
}
object Sum extends Expr {
  def apply(e1: Expr, e2: Expr) = new Sum(e1, e2)
}

这就是为什么我们可以编写例如Sum(...)并且仍然通过类实例化对象的原因,因为Sum(...)它也是Sum.apply(...).

我是对的,match构造也是语法糖吗?如果是,case Number(n)编译器是如何重写的?

我在问,因为我没有看到nin 在case Number(n)任何地方定义和/或绑定到一个值。奇怪的是,在match构造中,第一个字母的大小写很重要(如果它是大写字母,它将是一个常数)。这很奇怪,因为据我所知,这只是一个match相关的结构,所以我不知道如何去糖。

4

1 回答 1

5

是的,match是语法糖。它调用unapply对象上的方法。Daniel Westheide 有一篇关于它的不错的博客文章

具体来说,当您定义case classfor时Number,编译器实际生成的内容如下:

case class Number(n: scala.Int) extends scala.AnyRef with Expr with scala.Product with scala.Serializable {
  val n: scala.Int = { /* compiled code */ }
  /* omitted for brevity */
}
object Number extends scala.runtime.AbstractFunction1[scala.Int, Number] with scala.Serializable {
  def this() = { /* compiled code */ }
  final override def toString(): java.lang.String = { /* compiled code */ }
  def apply(n: scala.Int): Number = { /* compiled code */ }
  def unapply(x$0: Number): scala.Option[scala.Int] = { /* compiled code */ }
}

如您所见,Number伴生对象带有编译器生成的unapply方法。

您可以在此处找到有关 Scala 模式匹配工具设计的详尽描述。

- 编辑 -

如果您想查看编译器生成的实际代码,请运行scalac -print Number.scala. 以下是相关位:

<synthetic> object Number extends scala.runtime.AbstractFunction1 with Serializable {
  /* ... */
  case <synthetic> def unapply(x$0: Number): Option = if (x$0.==(null))
    scala.this.None
  else
    new Some(scala.Int.box(x$0.n()));
  /* ... */
}

如果您编写一个match表达式,您可以类似地运行scalac -print以查看其match本身是如何脱糖的(基本上是:ifelse表达式)。

于 2016-05-03T14:10:07.043 回答