这种案例类定义如何允许中缀模式匹配?

时间:2019-06-27 17:56:03

标签: scala pattern-matching case-class infix-notation parser-combinators

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

reading through the code期间,我看到~排序使用大小写类来保存左右值。

附有以下评论:

/** 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方法,但是这里没有提供。默认情况下,case类是否提供一个(我认为是)?如果是这样,这个特殊案例类如何变成case a ~ b而不是case ~(a,b)?这种模式可以被scala程序员利用吗?

1 个答案:

答案 0 :(得分:1)

  

案例类默认情况下是否提供[unapply](我认为是)?

您的怀疑是正确的。 unapply()是案例类自动提供的众多功能之一。您可以通过以下操作自行验证:

  1. 在自己的文件中写一个简单的class定义。
  2. 仅在“ typer”阶段编译文件并保存结果。 (调用scalac -Xshow-phases以查看所有编译器阶段的描述。)
  3. 编辑文件。在类定义之前添加单词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添加到类中,编译器就会生成大量额外的代码。

一些注意事项:

case class实例

  • 已将ProductSerializable混入类型
  • 提供对构造函数参数的公共访问权限
  • 具有方法copy()productArityproductElement()canEqual()
  • 重写(提供新代码)方法productPrefixproductIteratorhashCode()toString()equals()

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

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

如果是这样,这个特殊案例类如何变成case a ~ b而不是case ~(a,b)

事实证明,这是该语言提供的一种很好的便利(即使不太明显)。

unapply()调用返回一个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