这个 case class 定义如何允许中缀模式匹配?

How does this case class definition allow infix pattern matching?

我最近使用 scala parser combinator library 编写了一个解析器。我决定对实现感到好奇,然后开始挖掘。

reading through the code,我看到~排序用了一个caseclass来保存左右值

附上以下评论:

/** A wrapper over sequence of matches.
   *
   *  Given `p1: Parser[A]` and `p2: Parser[B]`, a parser composed with
   *  `p1 ~ p2` will have type `Parser[~[A, B]]`. The successful result
   *  of the parser can be extracted from this case class.
   *
   *  It also enables pattern matching, so something like this is possible:
   *
   *  {{{
   *  def concat(p1: Parser[String], p2: Parser[String]): Parser[String] =
   *    p1 ~ p2 ^^ { case a ~ b => a + b }
   *  }}}
   */
  case class ~[+a, +b](_1: a, _2: b) {
    override def toString = "("+ _1 +"~"+ _2 +")"
  }

既然提到的这样的代码肯定是可能的,并且使用a ~ b定义的解析器可以通过{ case a ~ b => ... }提取到值中,那么这个反应用程序究竟是如何工作的呢?我知道 scala 中的 unapply 方法,但此处提供了 none 。 case classes 是否默认提供一个(我想是的)?如果是这样,这个特殊情况 class 是如何变成 case a ~ b 而不是 case ~(a,b) 的?这是 Scala 程序员可以利用的模式吗?

这与此问题中的 不同,因为不存在 unapply 方法——或者存在吗? case classes 会自动神奇地接收未应用的方法吗?

Do case classes provide [unapply] by default (I think yes)?

你的怀疑是正确的。 unapply() 是案例 class 中自动提供的众多内容之一。您可以通过执行以下操作自行验证:

  1. 在自己的文件中写一个简单的 class 定义。
  2. 仅通过"typer"阶段编译文件并保存结果。 (调用 scalac -Xshow-phases 查看所有编译器阶段的描述。)
  3. 编辑文件。在 class 定义前添加单词 case
  4. 重复步骤 2。
  5. 比较两个保存的结果。

来自 Bash shell 它可能看起来像这样。

%%> cat srcfile.scala
class XYZ(arg :Int)
%%> scalac -Xprint:4 srcfile.scala > plainClass.phase4
%%> vi srcfile.scala  # add “case” 
%%> scalac -Xprint:4 srcfile.scala > caseClass.phase4
%%> diff plainClass.phase4 caseClass.phase4

编译器会产生大量噪音,但您会发现,只需将 case 添加到 class,编译器就会生成大量额外代码。

一些注意事项:

case class 实例

  • ProductSerializable 混合到类型
  • 提供public对构造函数参数的访问
  • 有方法 copy()productArityproductElement()canEqual()
  • 覆盖(提供新代码)方法 productPrefixproductIteratorhashCode()toString()equals()

伴随对象(由编译器创建)

  • 有方法 apply()unapply()
  • 覆盖toString()

If so, how does this particular case class become case a ~ b and not case ~(a,b)?

事实证明这是该语言提供的一个很好的(如果相当晦涩)便利。

unapply()调用returns一个Tuple可以被模式化为中缀符号。同样,这很容易验证。

class XX(val c:Char, val n:Int)
object XX {
  def unapply(arg: XX): Option[(Char, Int)] = Some((arg.c,arg.n))
}

val a XX b = new XX('g', 9)
//a: Char = g
//b: Int = 9