为什么 Scala PartialFunction 在不定义 isDefinedAt 的情况下工作?

Why Scala PartialFunction works without defining isDefinedAt?

看起来第一个第二个是一样的,但是为什么呢?

第一个

val iter = List(1, 2, 3, 4, 5).iterator
val first = iter.collect(new PartialFunction[Int, Int]{
  def apply(i: Int) = i
  def isDefinedAt(i: Int) = i > 0 && i < 3
})
first.foreach((println(_)))

第二

val iter2 = List(1, 2, 3, 4, 5).iterator
val second = iter2.collect {
  case i:Int if i > 0 && i < 3 => i
}
second.foreach((println(_)))

是不是因为Scala编译器自动转换 { case i:Int if i > 0 && i < 3 => i }First 的实现形式,从 if i > 0 && i < 3 部分生成 isDefinedAt?

此外,case i:Int if i > 0 && i < 3 => i 是 Case class 模式匹配,如果我是正确的话。但是,在 scala/src/library/scala/PartialFunction.scala 中,PartialFunction 没有 Case class 定义。

trait PartialFunction[-A, +B] extends (A => B)

那为什么这种情况 class 模式匹配有效?

我想 Scala 编译器可以智能地完成大量隐式工作,但它让我无法理解正在发生的事情以及如何编写 Scala 代码。

如果有很好的参考,而不是语言或编译器规范,可以理解 Scala代码语法和Scala写代码的方式,请指教。

是的,编译器将第二个版本转换为 PartialFunction[Int,Int](因为 collect 就是这样)。

这里没有匹配case class,甚至没有匹配类型,因为值必须是Int(因此不需要第二个版本中的类型声明)。

style guide 给出了很多关于 Scala 典型编写方式的提示。

Is it because the Scala compiler automatically converts { case i:Int if i > 0 && i < 3 => i } into the implelentation form of First with generating isDefinedAt from **if i > 0 && i < 3 ** part?

是的,Pattern Matching Anonymous Functions中给出了准确的翻译。在这里

new PartialFunction[Int, Int]{
  def apply(x: Int) = x match {
    case i:Int if i > 0 && i < 3 => i
  }
  def isDefinedAt(x: Int) = x match {
    case i:Int if i > 0 && i < 3 => true
    case _ => false
  } 
}

请注意与 apply 中的第一个示例的区别!当 isDefined 为 false 时,您 可以 仍然调用它。

Also, case i:Int if i > 0 && i < 3 => i is Case class pattern matching, if I am correct

如果说有什么不同的话,那就是反过来了; case 类 被这样调用是因为它们可以进行模式匹配,模式匹配在 Scala 中使用 case 关键字。

以你为例

object Main {
  def f = (1 to 5).collect { case i if i > 0 && i < 3 => i }
}

编译器生成的部分函数定义了 applyOrElse 因为它比天真的习语更有效:

if (pf.isDefinedAt(x)) pf.apply(x) else ???

显示该实现,与规范中描述的类似:

$ scalac -Vprint:typer pf.scala
[[syntax trees at end of                     typer]] // pf.scala
package <empty> {
  object Main extends scala.AnyRef {
    def <init>(): Main.type = {
      Main.super.<init>();
      ()
    };
    def f: IndexedSeq[Int] = scala.Predef.intWrapper(1).to(5).collect[Int](({
      @SerialVersionUID(value = 0) final <synthetic> class $anonfun extends scala.runtime.AbstractPartialFunction[Int,Int] with java.io.Serializable {
        def <init>(): <$anon: Int => Int> = {
          $anonfun.super.<init>();
          ()
        };
        final override def applyOrElse[A1 <: Int, B1 >: Int](x1: A1, default: A1 => B1): B1 = ((x1.asInstanceOf[Int]: Int): Int @unchecked) match {
          case (i @ _) if i.>(0).&&(i.<(3)) => i
          case (defaultCase$ @ _) => default.apply(x1)
        };
        final def isDefinedAt(x1: Int): Boolean = ((x1.asInstanceOf[Int]: Int): Int @unchecked) match {
          case (i @ _) if i.>(0).&&(i.<(3)) => true
          case (defaultCase$ @ _) => false
        }
      };
      new $anonfun()
    }: PartialFunction[Int,Int]))
  }
}

其中 AbstractPartialFunction 定义

def apply(x: T1): R = applyOrElse(x, PartialFunction.empty)

这里是an external link to a change to use applyOrElse. The improved PartialFunction dates back to 2012. Probably the feature is under-documented or under-advertised. Some information is available by expanding the Scaladoc for PartialFunction。出于某种原因,link 显示 orElse,因此您实际上必须向后滚动才能找到 applyOrElse。看来文档很难。