zoukankan      html  css  js  c++  java
  • Scala的Pattern Matching Anonymous Functions

    参考自http://stackoverflow.com/questions/19478244/how-does-a-case-anonymous-function-really-work-in-scala

    http://www.scala-lang.org/files/archive/nightly/pdfs/ScalaReference.pdf

    http://docs.scala-lang.org/overviews/core/futures.html

    在第三篇文档《Futures and Promises》中,讲到了Future对象有三个方法可以注册callback

    import scala.util.{Success, Failure}
    
    val f: Future[List[String]] = future {
      session.getRecentPosts
    }
    
    f onComplete {
      case Success(posts) => for (post <- posts) println(post)
      case Failure(t) => println("An error has occured: " + t.getMessage)
    }
    
    f onFailure {
      case t => println("An error has occured: " + t.getMessage)
    }
    
    f onSuccess {
      case posts => for (post <- posts) println(post)
    }

    传给onComplete、onFailture和onSuccess的都是

    { case p1 => b1 ... case pn => bn }

    形式的语句,但是这三个方法接受的参数类型却是不同的。

    abstract def onComplete[U](f: (Try[T]) ⇒ U)(implicit executor: ExecutionContext): Unit

    def onSuccess[U](pf: PartialFunction[T, U])(implicit executor: ExecutionContext): Unit

    def onFailure[U](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Unit

    onCompelete的参数类型的是一个 (Try[T]) => U函数, 而onSuccess和onFailure的参数类型是偏函数。

    那么,问题来了……{ case p1 => b1 ... case pn => bn } 的类型到底是啥呢?

    在<The Scala Language Specification>的第8.5章给出了说明:

    An anonymous function can be defined by a sequence of cases

    {case p1 =>b1 ...case pn =>bn }

    which appear as an expression without a prior match.
    The expected type of such an expression must in part be defined. It must be either scala.Functionk[S1, ..., Sk, R] for some k >0, or scala.PartialFunction[S1, R], where the argument type(s) S1, ..., Sk must be fully determined, but the result type R may be undetermined. 

    也就是说{ case p1 => b1 ... case pn => bn } 这种表达式的值的类型可以有两种,要不是一个函数,要不是一个偏函数(偏函数也是一种函数)。在这个表达式的位置上需要哪种类型,编译器就会用这个表达式生成对应的类型。但是无论是生成函数还是偏函数,它们的参数的类型都必须是确定的,对于一个特定的Future对象,onComplete接受的函数的参数类型是Try[T],而onSuccess接受的PartialFunction的参数类型是T,onFailure接受的PartialFunction的参数类型是Throwable。但是这些函数的返回类型U可以不是需要这个{ case p1 => b1 ... case pn => bn } 表达式的地方指定的。比如,这三个onXXX方法都没有指定它所接受的函数的返回值类型。

    例子:

    import java.io.IOException
    
    import scala.concurrent._
    import scala.concurrent.ExecutionContext.Implicits.global
    import scala.util.Success
    import scala.util.Failure
    
    
    object CallbacksOfFuture extends App {
    
      def getRecentPosts = {
        Thread.sleep(5000)
        "Good morning" :: "Good afternoon" :: Nil
        throw new TimeoutException("Goodbye")
      }
    
      val f: Future[List[String]] = future {
        val posts = getRecentPosts
        posts
      }
    //onComplete A f onComplete {
    case Success(posts) => posts.foreach(println) case Failure(e) => println("An error has occured: " + e.getMessage) }
    //onComplete B f onComplete { result
    => result match { case Success(posts) => posts.foreach(println) case Failure(e) => println("An error has occured: " + e.getMessage) } } //won't compile // f onComplete{ // case 1 => 2 // } f onSuccess { case posts => posts foreach println } f onFailure{ case r: IOException => println("got IOException: " + r.getMessage) case r: TimeoutException => println("got TimeoutException: " + r.getMessage) case e => println("An error has occured: " + e.getMessage) } f flatMap{ posts => future{posts}} foreach(println) f map (posts => posts) foreach println Thread.sleep(10000) }

     那么onComplete和onSuccess、onFailure还有啥不同呢?

    如果我们把onComplete A实现里的case Failure去掉,那么在运行时就会报MatchError,因为

    f onComplete {
        case Success(posts) => posts.foreach(println)
        case Failure(e) => println("An error has occured: " + e.getMessage)
      }

    实际上会被翻译为:

     f onComplete { result =>
        result match {
          case Success(posts) => posts.foreach(println)
          case Failure(e) => println("An error has occured: " + e.getMessage)
        }
      }

    当result是一个Failure,那么在去掉case Failure后,它会无法得到匹配,从而报出MatchError.

    而在

      f onFailure{
        case r: IOException => println("got IOException: " + r.getMessage)
        case r: TimeoutException => println("got TimeoutException: " + r.getMessage)
        case e => println("An error has occured: " + e.getMessage)
      }

    如果我们只留下case r: IOException,虽然运行时产生的异常是TimeoutException,但是执行时却不会报错。这是为啥呢?

    看Future的源码吧

     def onFailure[U](callback: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Unit = onComplete {
        case Failure(t) =>
          callback.applyOrElse[Throwable, Any](t, Predef.conforms[Throwable]) // Exploiting the cached function to avoid MatchError
        case _ =>
      }

    原来onFailure会将callback注册给onComplete,这使得调用onFailure也不会阻塞。当Future的执行结果为Failure时,Failure中包装的异常会被apply给t, 如果apply失败,会执行Predef.confirm[Throwable]。这个函数是这样的:

     sealed abstract class <:<[-From, +To] extends (From => To) with Serializable
      private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x }
      // not in the <:< companion object because it is also
      // intended to subsume identity (which is no longer implicit)
      implicit def conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]

    在这里,confirm的类型参数为Throwable,于是 singleton_<:<.asInstanceOf[A <:< A]被类型为换为 <:<[Throwable <:< Throwable]。

    singleton_<:<本身是一个对象,它的超类的类型是 Any => Any,因此,singleton_<:<.asInstanceOf[Throwable <:< Throwable]是一个类型为(Throwable) => Throwable的函数。因此conform在onFailure中的使用是类型正确的。

    那么onform返回的这个函数干了啥呢,它的apply方法接收x,返回x。用于onFailure的环境中时,就相当于

    def onFailure[U](callback: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Unit = onComplete {
        case Failure(t) =>
          callback.applyOrElse[Throwable, Any](t, (e) => e) // Exploiting the cached function to avoid MatchError
        case _ =>
      }

    在callback.applyOrElse方法中,我们需要一个函数,它的类型是(Throwable) => Any,又没有副作用。那么用Predef.conform[Throwable]得到一个实际上啥都没干的(Throwable) => (Throwable)是很合适的。

    应该说这么写挺规范吧……

  • 相关阅读:
    TOPCoder(一)Time
    highchart柱状图 series中data的数据构造
    (转)myeclipse工程 junit不能运行 ClassNotFoundException
    reserve和resize区别
    ++ fatal error C1083: 无法打开预编译头文件:“.Debug outer.pch”
    初学lua --lua嵌入c++的一个问题(初始化lua出错,版本问题)
    .NET中字符串split的C++实现
    成员函数指针与高效C++委托 (delegate)
    Android.mk 用法介绍
    cocos2d-x学习之旅(五):1.5 使用eclipse编译cocos2d-x示例项目,创建cocos2d-x android项目并部署到真机
  • 原文地址:https://www.cnblogs.com/devos/p/4108212.html
Copyright © 2011-2022 走看看