参考自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. xml初读

    形式良好的 XML 文档 “形式良好”或“结构良好”的 XML 文档拥有正确的语法. “形式良好”(Well Formed)的 XML 文档会遵守前几章介绍过的 XML 语法规则: XML 文档必须有 ...

  2. 牛客_Java_值传递(拷贝)不该表原来变量+传引用的话会一起改变

    总结: 许多编程语言都有2种方法将参数传递给方法------按值传递和按引用传递.  与其他语言不同,Java不允许程序员选择按值传递还是按引用传递各个参数,基本类型(byte--short--int ...

  3. 什么是C++标准库?

    C++中的标准程序库(简称标准库)是类库和函数的集合,其使用核心语言写成.标准程序库提供若干泛型容器.函数对象.泛型字符串和流(包含交互和文件I/O),支持部分语言特性和常用的函数,如开平方根.C++ ...

  4. 第五十八篇、iOS 微信聊天发送小视频的秘密

    对于播放视频,大家应该一开始就想到比较方便快捷使用简单的MPMoviePlayerController类,确实用这个苹果官方为我们包装好了的 API 确实有很多事情都不用我们烦心,我们可以很快的做出一 ...

  5. (转)Mongodb相对于关系型数据库的优缺点

    与关系型数据库相比,MongoDB的优点:①弱一致性(最终一致),更能保证用户的访问速度:举例来说,在传统的关系型数据库中,一个COUNT类型的操作会锁定数据集,这样可以保证得到“当前”情况下的精确值 ...

  6. 图像热点&图像映射

    图像映射 图像映射也称为图像热点. 作用: 让同一张图片上的不同区域,可以实现多个不同的超链接功能. 图示: <map>图像映射三步走: 图像映射的实现需要三方面配合完成: 1.图像映射容 ...

  7. lex&yacc2

    YACC: 每个归约后yacc 都执行默认动作,在运行任何明确的动作代码之前,将值$1 赋介$$. 下面是从这个语法中生成的 y.tab.h:#define NAME 257#define NUMBE ...

  8. 清理c盘垃圾(将一下代码复制到记事本然后把后缀名改为xxx.bat,然后双击,就ok了!!)

    @echo off echo 正在清除系统垃圾文件,请稍等...... del /f /s /q %systemdrive%\*.tmp del /f /s /q %systemdrive%\*._m ...

  9. python pil 安装

    Ubuntu下 sudo pip install pil 安装PIL可能会出现问题,例如安装完成时显示JPEG support not available 或者 ZLIB (PNG/ZIP) supp ...

  10. nand flash相关

    关于nandflash的说明,请参考其他. 现在先贴出来韦东山先生的代码,作我学习之用. @************************************************ @ Fil ...