orElse如何处理PartialFunctions

时间:2014-08-18 21:48:28

标签: scala functional-programming read-eval-print-loop partialfunction

orElse

定义的PartialFunction方法让我的行为非常离奇(至少在我看来)

在我看来:

val a = PartialFunction[String, Unit] {
    case "hello" => println("Bye")
}
val b: PartialFunction[Any, Unit] = a.orElse(PartialFunction.empty[Any, Unit])
a("hello") // "Bye"
a("bogus") // MatchError
b("bogus") // Nothing
b(true)    // Nothing

有意义,但这不是它的表现方式,我在理解为什么类型签名似乎表明我在上面暴露的内容时遇到了很多麻烦。

以下是我使用Scala 2.11.2观察的内容的记录:

Welcome to Scala version 2.11.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_11).
Type in expressions to have them evaluated.
Type :help for more information.

scala> val a = PartialFunction[String, Unit] {
     | case "hello" => println("Bye")
     | }
a: PartialFunction[String,Unit] = <function1>

scala> a("hello")
Bye

scala> a("bye")
scala.MatchError: bye (of class java.lang.String)
  at $anonfun$1.apply(<console>:7)
  at $anonfun$1.apply(<console>:7)
  at scala.PartialFunction$$anonfun$apply$1.applyOrElse(PartialFunction.scala:242)
  at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36)
  ... 33 elided

scala> val b = a.orElse(PartialFunction.empty[Any, Unit])
b: PartialFunction[String,Unit] = <function1>

scala> b("sdf")
scala.MatchError: sdf (of class java.lang.String)
  at $anonfun$1.apply(<console>:7)
  at $anonfun$1.apply(<console>:7)
  at scala.PartialFunction$$anonfun$apply$1.applyOrElse(PartialFunction.scala:242)
  at scala.PartialFunction$OrElse.apply(PartialFunction.scala:162)
  ... 33 elided

请注意val b的返回类型,它没有扩大PartialFunction的类型。

但这也不能按预期工作:

scala> val c = a.orElse(PartialFunction.empty[String, Unit])
c: PartialFunction[String,Unit] = <function1>

scala> c("sdfsdf")
scala.MatchError: sdfsdf (of class java.lang.String)
  at $anonfun$1.apply(<console>:7)
  at $anonfun$1.apply(<console>:7)
  at scala.PartialFunction$$anonfun$apply$1.applyOrElse(PartialFunction.scala:242)
  at scala.PartialFunction$OrElse.apply(PartialFunction.scala:162)
  ... 33 elided

3 个答案:

答案 0 :(得分:28)

你的尝试有一些问题,但首先让我们看看一个有效的实施:

scala> val a: PartialFunction[String, Unit] = { case "hello" => println("bye") }
a: PartialFunction[String,Unit] = <function1>

scala> val b: PartialFunction[Any, Unit] = { case _ => println("fallback") }
b: PartialFunction[Any,Unit] = <function1>

scala> val c = a.orElse(b)
c: PartialFunction[String,Unit] = <function1>

scala> c("hello")
bye

scala> c("foo")
fallback

您的代码中存在两个主要错误:

  1. 定义PF的方式
  2. (错误的)假设empty是一个“全能”函数,返回Nothing
  3. 1。如何定义PartialFunction

    val right: PartialFunction[String, Unit] = {
      case "hello" => println("bye")
    }
    

    如何来定义它:

    val wrong = PartialFunction[String, Unit] {
      case "hello" => println("bye")
    }
    

    如果你看一下PartialFunction.apply

    的定义
    def apply[A, B](f: A => B): PartialFunction[A, B] = { case x => f(x) }
    

    您会看到它为任何 x定义了部分函数,​​并将给定的f函数应用于该函数。现在,您的{ case "hello" => println("bye") }f参数,因此您最终会得到以下结果(显然是意外的)PartialFunction

    val wrong: PartialFunction[String, Unit] = {
      case x => x match {
        case "hello" => println("bye")  
      }
    

    因此,当您询问它是否已定义时,它将始终返回true,因为它是为任何字符串定义的:

    wrong.isDefinedAt("hello") // true (ok)
    wrong.isDefinedAt("whatever") // true (sure?)
    

    但是当你尝试apply

    wrong("hello") // bye (ok)
    wrong("whatever") // MatchError (BOOM!)
    

    你没有达到内部匹配。

    由于orElse决定是否根据isDefined的结果调用“else”,因此很明显它失败的原因。

    2。空无所事!

    直接来自docs

      

    def empty[A, B]: PartialFunction[A, B]

         

    具有空域的部分功能。任何调用空部分函数的尝试都会导致抛出scala.MatchError异常。

    你正在寻找的PartialFunction(好吧,它不再是部分的):

    val fallback: PartialFunction[Any, Unit] = { case _ => println("fallback") }
    

    或 - 只是为了表明我们从错误中吸取教训 -

    val fallback = PartialFunction[Any, Unit] { _ => println("fallback") }
    

答案 1 :(得分:2)

您正在使用PartialFunction对象应用方法,该方法已定义:

def apply[A, B](f: A => B): PartialFunction[A, B] = { case x => f(x) }

基本上它需要一个函数形式AB并自动将它包装在一个case语句中,问题是你也通过了这个案例而且我不能100%肯定会发生什么然后,您可以尝试将函数传递给apply或者您可以在不使用apply方法的情况下轻松尝试:

scala>   val a: PartialFunction[String, Unit] = {
     |     case "hello" => println("Bye")
     |   }
a: PartialFunction[String,Unit] = <function1>

scala>   val b: PartialFunction[String, Unit] =  {
     |     case _ => println("default")
     |   }
b: PartialFunction[String,Unit] = <function1>

scala> b("123")
default

您还可以扩展特征并实施applyisDefined,如图所示here

答案 2 :(得分:1)

PartialFunction.empty[A,B]相当于:

{
  case x: Nothing => x
}

(这个类型检查,因为NothingAB的子类型。)

或等同于:

{
  // note: this is probably not a valid Scala code for a partial function
  // but, as PartialFunction.empty's name suggests, it's an *empty* block
} 

这无法匹敌。

.orElse可以被理解为简单地连接来自两个PartialFunction的案例陈述列表。因此,在您的情况下,a.orElse(PartialFunction.empty[Any,Unit]表示:

{ case "hello" => println("Bye") } orElse { /* no cases here */ }

简化为:

{ case "hello" => println("Bye") }

{ case "hello" => println("Bye"); case x:Nothing => x }

MatchError因此很明显。

请注意,the documetation还提到empty始终抛出MatchError


根据我的猜测,你想要一个始终匹配的PartialFunction。标准库中没有命名方法,但为什么要这样。你可以简单地写

{ case _ => () }