为什么定义了这个PartialFunction,但在Scala中应用后仍然崩溃(正确)?

为什么定义了这个PartialFunction,但在Scala中应用后仍然崩溃(正确)?,scala,partialfunction,Scala,Partialfunction,我想尝试使用深度模式匹配用例的部分函数。这一点最初(当然)在应用了一些(一些(3))之后不起作用,但似乎被定义为: def deepTest : PartialFunction [Option[Option[Int]], Int] = { case Some(v) => v match { case None => 3 } case None => 1 } 我认为通过解耦嵌套模式匹配,事情会更容易: def deepTestLvl1 :

我想尝试使用深度模式匹配用例的部分函数。这一点最初(当然)在应用了一些(一些(3))之后不起作用,但似乎被定义为:

def deepTest : PartialFunction [Option[Option[Int]], Int] = {
    case Some(v) => v match {
      case None => 3 
    }
    case None => 1
}
我认为通过解耦嵌套模式匹配,事情会更容易:

def deepTestLvl1 : PartialFunction [Option[Option[Int]], Option[Int]] = {
  case Some(v) => v
  case None => Some(1)
}


def deepTestLvl2 : PartialFunction [Option[Int], Int] = {
  case None => 3
}
但结果如下:

scala> (deepTestLvl1 andThen deepTestLvl2) isDefinedAt(Some(Some(3)))
res24: Boolean = true
申请后:

scala> (deepTestLvl1 andThen deepTestLvl2) (Some(Some(3)))
scala.MatchError: Some(3) (of class scala.Some)
    at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:248)
    at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:246)
    at $anonfun$deepTestLvl2$1.applyOrElse(<console>:7)
    at $anonfun$deepTestLvl2$1.applyOrElse(<console>:7)
        ....
    at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:83)
    at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:96)
    at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:105)
    at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
scala>(deepTestLvl1和deepTestLvl2)(一些(一些(3)))
scala.MatchError:Some(3)(属于scala.Some类)
在scala.PartialFunction$$anon$1.apply处(PartialFunction.scala:248)
在scala.PartialFunction$$anon$1.apply处(PartialFunction.scala:246)
在$anonfun$deepTestLvl2$1.applyOrElse(:7)
在$anonfun$deepTestLvl2$1.applyOrElse(:7)
....
位于scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:83)
在scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:96)中
位于scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:105)
位于scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)

我做错什么了吗?当我按顺序编写deepTestLvl{1,2}并给出正确答案时,isDefinedAt不应该被调用两次吗?

生成的
部分函数
进行isDefinedAt
的原因是它实际上没有将第一个部分函数应用于它的参数,这可能是一个昂贵的手术

这种行为可能会让人绊倒,而且没有文档记录——您可能需要提交一个补丁来添加文档

另外,我的猜测是,
deepTest
之所以会这样做,是因为部分函数定义的源代码中最外层的
match
,并且只有最外层的匹配,被认为是用于定义的目的-但是您必须检查
scalac
的源代码才能确定,我想。

这是一个很好的问题

让我们检查来源,看看封面下发生了什么:

override def andThen[C](k: B => C): PartialFunction[A, C] =
  new AndThen[A, B, C] (this, k)
我们可以在这里观察到,
,然后
甚至不需要部分函数,任何转换结果的函数都可以。您的代码可以工作,因为:
trait PartialFunction[-A,+B]扩展了(A=>B)
。这实际上可以在以下内容中找到:

def和第[C](k:(B)⇒ C) :部分功能[A,C]

将此分部函数与应用于此分部函数结果的转换函数组合

C转换函数的结果类型

k变换函数

返回与此分部函数具有相同域的分部函数,该分部函数将参数
x
映射到
k(此(x))

因此,目前没有办法以您希望的方式链接
PartialFunction
s,因为正如Robin所说,这需要应用该函数。除了计算成本高之外,它还可能产生副作用,这是一个更大的问题

更新 拼凑出一个您正在寻找的实现小心使用正如我已经提到的,如果您的代码有副作用,它将导致问题:

implicit class PartialFunctionExtension[-A, B](pf: PartialFunction[A, B]) {
  def andThenPf[C](pf2: PartialFunction[B, C]) = new PfAndThen(pf, pf2)

  class PfAndThen[+C](pf: PartialFunction[A, B], nextPf: PartialFunction[B, C]) extends PartialFunction[A, C] {
    def isDefinedAt(x: A) = pf.isDefinedAt(x) && nextPf.isDefinedAt(pf.apply(x))

    def apply(x: A): C = nextPf(pf(x))
  }
}
试一试:

deepTestLvl1.andThenPf(deepTestLvl2).isDefinedAt(Some(Some(3)))  // false
deepTestLvl1.andThenPf(deepTestLvl2).isDefinedAt(Some(None))     // true
deepTestLvl1.andThenPf(deepTestLvl2).apply(Some(None))           // 3

不幸的是,Scala语言规范中没有正确地记录这一点-请参阅,它可以工作,但没有真正完整地解释。有人知道一个实际的引用,它正确地记录了Scala部分函数的当前行为吗?@RobinGreen请检查我的答案。它没有文档记录,因为它不是
的定义方式,
。@krivachy.akos我指的是
deepTest
。啊,在这种情况下,我认为您必须实现两个部分函数的所有组合<代码>案例部分(无)=>3和
案例部分(部分(v))=>v
。这并不太优雅,特别是考虑到案例数量可以呈指数增长的事实。在Coursera的“反应式编程原理”中,Martin Odersky解释说,实际上只考虑最外层的匹配。