参考自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. Android 如何监听返回键,弹出一个退出对话框

    android 如何监听返回键点击事件,并创建一个退出对话框, 防止自己写的应用程序不小心点击退出键而直接退出.自己记录下这个简单的demo,备用. public class BackKeyTest ...

  2. vs2010打包winform程序详解

    vs2010打包winform程序详解   最近一直在做winform程序,做完后程序打包很头疼,第三方打包工具,好用的花钱,不花钱的不好用,最后只能用vs自带的打包工具了! 打包过程vs2010(包 ...

  3. Java之趣味编程结婚问题

    问题如下:判断结婚的组合对数数.定义: 好三位新郎为 A,B,C ;三位新娘为X,Y,Z 有人想要知道他们谁和谁结婚 ,于是问了其中的三位. 回答是这样的:A说他将和X结婚 :X说她的未婚夫是C ;C ...

  4. JAVA之数据溢出

    Integer在java中属于包装类,既能用于字符串与整型的转换,也能用于拆箱与装箱 package ABC; public class A{ public static void main(Stri ...

  5. Hibernate+DWR无刷新三级联动

    DWR(Direct Web Remoting)是一个用于改善web页面与Java类交互的远程服务器端Ajax开源框架,可以帮助开发人员开发包含AJAX技术的网站.它可以允许在浏览器里的代码使用运行在 ...

  6. iOS夯实:内存管理

    iOS夯实:内存管理 文章转自 内存管理 最近的学习计划是将iOS的机制原理好好重新打磨学习一下,总结和加入自己的思考. 有不正确的地方,多多指正. 目录: 基本信息 旧时代的细节 新时代 基本信息 ...

  7. .Net Core 项目中的包引用探索(使用VSCode)

    本文组织有点乱,先说结论吧: 1 在 project.json 文件中声明包引用. 而不是像以前那样可以直接引用 dll. 2 使用 dotnet restore 命令后,nuget 会把声明的依赖项 ...

  8. 6.JAVA_SE复习(集合)

    集合 结构图: 总结: 1.集合中的元素都是对象(注意不是基本数据类型),基本数据类型要放入集合需要装箱. 2.set与list的主要区别在于set中不允许重复,而list(序列)中可以有重复对象. ...

  9. Mac OS 踩坑指南

    前言 其实mac os本身还是很不错的,软硬结合使得其性能.效率.续航得到了很好的优化. 但是毕竟是一个"小众"操作系统,很多在Win上已经用习惯的东西在这里都没有,或者完全不一样 ...

  10. 如何利用VS2010安装和部署应用程序

    转自:http://jingyan.baidu.com/article/4b52d70255d7f0fc5d774b4d.html 1.假设你当前的winform已经okay了 2.解决方案中新建&q ...