代码之家  ›  专栏  ›  技术社区  ›  jedesah

orElse如何处理PartialFunctions

  •  10
  • jedesah  · 技术社区  · 10 年前

    我的行为很奇怪(至少在我看来) 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 回复  |  直到 10 年前
        1
  •  28
  •   Community CDub    4 年前

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

    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

    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 根据结果决定是否调用“else” isDefined 那么它失败的原因就很明显了。

    2.空虚什么也抓不到!

    直接从 docs :

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

    域为空的分部函数。任何调用空部分函数的尝试都会导致抛出 scala.MatchError 例外

    这个 部分功能 (好吧,它不再是真正的部分)你要找的是:

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

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

    val fallback = PartialFunction[Any, Unit] { _ => println("fallback") }
    
        2
  •  2
  •   Ende Neu    10 年前

    您正在使用 PartialFunction 对象应用方法,其定义如下:

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

    基本上它采用函数形式 A B 并自动将其包装在case语句中,问题是您也在传递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
    

    您还可以扩展特性并实现 apply isDefined 如图所示 here .

        3
  •  1
  •   Karol S    10 年前

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

    {
      case x: Nothing => x
    }
    

    (这种类型检查,因为 Nothing 是两者的子类型 A B .)

    或等效地:

    {
      // 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 可以理解为简单地连接两个case语句的列表 PartialFunction s、 所以,在你的情况下 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 总是抛出 匹配错误 .


    根据我的猜测,你想要 部分功能 总是匹配的。标准库中没有命名方法,但为什么要这样呢

    { case _ => () }
    
    推荐文章