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

时间:2013-11-23 18:56:47

标签: scala partialfunction

我想尝试使用深度模式匹配用例的部分函数。最初(当然)在应用Some(Some(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)

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

2 个答案:

答案 0 :(得分:3)

非常好的问题。

让我们检查来源,看看幕后发生了什么:

override def andThen[C](k: B => C): PartialFunction[A, C] =
  new AndThen[A, B, C] (this, k)

我们可以在此观察andThen 甚至不期望部分功能,任何转换结果的函数都可以。您的代码有效,因为:trait PartialFunction[-A, +B] extends (A => B)。这实际上可以在the documentation

中找到
  

def andThen[C](k: (B) ⇒ C): PartialFunction[A, C]

     

使用转换函数组合此部分函数,​​该函数将应用于此部分函数的结果。

     

C 转换函数的结果类型。

     

k 转换函数

     

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

因此,目前无法按照您希望的方式链接PartialFunction,因为正如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

答案 1 :(得分:1)

isDefinedAt生成的PartialFunction andThen返回不一致结果的原因是它实际上并未将第一个部分函数应用于其参数 ,这可能是一项昂贵的操作。

这种行为可能会引起人们注意并且没有记录 - 您可能需要提交补丁来为其添加文档。

P.S。我的猜测是deepTest的行为原因是,部分函数定义的源代码中最外层match,而最外层匹配,被认为是出于定义目的 - 但我认为你必须检查scalac的源代码。