参考自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)是很合适的。

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

Scala的Pattern Matching Anonymous Functions的更多相关文章

  1. Beginning Scala study note(5) Pattern Matching

    The basic functional cornerstones of Scala: immutable data types, passing of functions as parameters ...

  2. [Scala] Pattern Matching(模式匹配)

    Scala中的match, 比起以往使用的switch-case有著更強大的功能, 1. 傳統方法 def toYesOrNo(choice: Int): String = choice match ...

  3. scala pattern matching

    scala语言的一大重要特性之一就是模式匹配.在我看来,这个怎么看都很像java语言中的switch语句,但是,这个仅仅只是像(因为有case关键字),他们毕竟是不同的东西,switch在java中, ...

  4. SCALA XML pattern attrbute(属性)

    from: Working with Scala's XML Support 虽然这个guy炒鸡罗嗦,但是还是讲到我要的那句话:  Because Scala doesn't support XML ...

  5. Symbols of String Pattern Matching

    Symbols of String Pattern Matching in Introduction to Algorithms. As it's important to be clear when ...

  6. php中的匿名函数(Anonymous functions)和闭包函数(closures)

    一:匿名函数 (在php5.3.0 或以上才能使用) php中的匿名函数(Anonymous functions), 也叫闭包函数(closures), 允许指定一个没有名称的函数.最常用的就是回调函 ...

  7. Zhu-Takaoka Two-dimensional Pattern Matching

    Two dimensional pattern matching. Details may be added later.... Corresponding more work can be foun ...

  8. [PureScript] Break up Expressions into Cases in PureScript using Simple Pattern Matching

    Pattern matching in functional programming languages is a way to break up expressions into individua ...

  9. PHP基础知识之————匿名函数(Anonymous functions)

    匿名函数(Anonymous functions),也叫闭包函数(closures),允许 临时创建一个没有指定名称的函数.最经常用作回调函数(callback)参数的值.当然,也有其它应用的情况. ...

随机推荐

  1. 和阿文一起学H5--如何把H5压缩到最小

    三种压缩图片的方法: 1.PS 但是PS每次只能压缩一张,下面介绍第二个神器 2.TinyPng压缩 https://tinypng.com/ 3.IloveIMG压缩 http://www.ilov ...

  2. cv::mat转换成QImage的问题

    在进行cv::mat转换为QImage过程中,经常出现问题: cv::Mat image; ...QImage img=QImage((const unsigned char*)(image.data ...

  3. OpenGL5-纹理贴图

    代码下载 #include "CELLWinApp.hpp"#include <gl/GLU.h>#include <assert.h>#include & ...

  4. nodejs包管理

    包管理 :npm cnpm yarn Node Package Manager node的包管理工具 cls清除控制台 npm install jquery 下载依赖包 就自动下载最新版本的jq np ...

  5. javascrip笔记——图片加载

    var t_img; // 定时器 var isLoad = true; // 控制变量 // 判断图片加载状况,加载完成后回调 isImgLoad(function(){ // 加载完成 }); / ...

  6. CAF(C++ actor framework)(序列化之结构体,任意嵌套STL)(一)

    User-Defined Data Types in Messages(用户自定义类型)All user-defined types must be explicitly “announced” so ...

  7. Bugzilla 使用指南

    Bugzilla安装见前一篇博客,本篇文章主要关注于如何高效合理的使用Bugzilla,作为为公司内部人员的培训使用指南. Bugzilla是一个开源的缺陷跟踪系统,它可以管理软件开发过程中缺陷的提交 ...

  8. 从 Java 代码逆向工程生成 UML 类图和序列图

    from:http://blog.itpub.net/14780914/viewspace-588975/ 本文面向于那些软件架构师,设计师和开发人员,他们想使用 IBM® Rational® Sof ...

  9. TweenMax动画库学习(一)

    目录            TweenMax动画库学习(一)            TweenMax动画库学习(二)            TweenMax动画库学习(三)            Tw ...

  10. JavaScript异步获取数据解析

    <script type="text/javascript">    var xhr=false;    function createXhr() {    var x ...