参考自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. C# IO操作(五)文件的递归加载

    本篇是一个案例,其核心通过代码展示代码中的递归这个用法,程序的界面如下:

  2. JS调用iframe方式实现Web区域打印页面内容

    1.程序说明 1) 此程序可以实现选择页面中的区域进行打印,以iframe方式进行打印: 2) 与原生态的print() 区别在于,取消打印页面后可以完整保留当前访问页面的内容. 2.代码部分 1) ...

  3. Memcached学习(三)

    通过Java客户端实现与Memcached的交互,Java客户端的实现了使用了开源的Memcached-Java-Client,开源地址在GitHub上. 如下是通过该开源库实现的Memcached交 ...

  4. 第五十六篇、OC打开本地和网络上的word、ppt、excel、text等文件

    iOS打开本地和网络上的word.ppt.excel.text等文件 iOS开发过程中可能需要预览一些文件,这些文件的格式可能有word.ppt.excel等文件格式.那么系统提供两个类去预览这些文件 ...

  5. Quartz 第二课 Jobs and Triggers(官方文档翻译)

    The Quartz API IScheduler—与scheduler交互的主要的接口 IJob—这个接口主要定义scheduler执行内容 IJobDetail—用于定义Jobs实例 ITrigg ...

  6. (转)mysql、sqlserver、oracle的默认事务的隔离级别

    1.mysql的默认事务的隔离级别:可重复读取(repeatable read); 2.sqlserver的默认事务的隔离级别:提交读取(read committed); 3.oracle的默认事务的 ...

  7. Powerful Sleep(神奇的睡眠-睡眠生物钟的秘密:如何睡得更少却睡得更好)阅读笔记

    睡眠机制 我们活着的时候,大脑会产生脑电波.脑电图仪器通过贴在人头上的一些电极读出脑电波的活动,然后把活动用图表显示出来. 睡眠过程可以分为5个过程,划分依据与大脑发出的脑电波类型. 当人清醒时,大脑 ...

  8. Paxos算法

    Paxos算法是分布式系统中常用的一个保持系统一致性的算法,由美国计算机科学家Leslie B. Lamport提出.原文链接. 今天特意学习了一下Paxos的原理,为防忘记,记录下来.(看了的东西没 ...

  9. phpstorm配置取消掉63342

    http://ask.csdn.net/questions/171665

  10. 35 个必须有的Bootstrap工具和生成器

    Bootstraptor If you think that bootstrap templates are not enough for you, you should go with bootst ...