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)是很合适的。
应该说这么写挺规范吧……
Scala的Pattern Matching Anonymous Functions的更多相关文章
- Beginning Scala study note(5) Pattern Matching
The basic functional cornerstones of Scala: immutable data types, passing of functions as parameters ...
- [Scala] Pattern Matching(模式匹配)
Scala中的match, 比起以往使用的switch-case有著更強大的功能, 1. 傳統方法 def toYesOrNo(choice: Int): String = choice match ...
- scala pattern matching
scala语言的一大重要特性之一就是模式匹配.在我看来,这个怎么看都很像java语言中的switch语句,但是,这个仅仅只是像(因为有case关键字),他们毕竟是不同的东西,switch在java中, ...
- SCALA XML pattern attrbute(属性)
from: Working with Scala's XML Support 虽然这个guy炒鸡罗嗦,但是还是讲到我要的那句话: Because Scala doesn't support XML ...
- Symbols of String Pattern Matching
Symbols of String Pattern Matching in Introduction to Algorithms. As it's important to be clear when ...
- php中的匿名函数(Anonymous functions)和闭包函数(closures)
一:匿名函数 (在php5.3.0 或以上才能使用) php中的匿名函数(Anonymous functions), 也叫闭包函数(closures), 允许指定一个没有名称的函数.最常用的就是回调函 ...
- Zhu-Takaoka Two-dimensional Pattern Matching
Two dimensional pattern matching. Details may be added later.... Corresponding more work can be foun ...
- [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 ...
- PHP基础知识之————匿名函数(Anonymous functions)
匿名函数(Anonymous functions),也叫闭包函数(closures),允许 临时创建一个没有指定名称的函数.最经常用作回调函数(callback)参数的值.当然,也有其它应用的情况. ...
随机推荐
- VS2010与QT的集成开发环境
http://blog.csdn.net/hbsong75/article/details/9293773 QT与Java有点类似,也是一种跨平台的软件(当然在windows平台和Linux平台需要安 ...
- 学习Learn Python The Hard Way 前言中的一段话,可与君共勉
在你通过这本书学习编程时,我正在学习弹吉他.我每天至少训练 2 小时,至少花一个小时练习音阶.和声.和琶音,剩下的时间用来学习音乐理论和歌曲演奏以及训练听力等.有时我一天会花 8 个小时来练习,因为我 ...
- 第三十一篇、iOS 9版本适配
1.网络适配(强制回退HTTP) 为了强制增强数据访问安全, iOS9 默认会把 所有的http请求 所有从NSURLConnection . CFURL . NSURLSession发出的 HTTP ...
- 改变UITextField placeHolder 字体 颜色
[_textSearchField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"]; ...
- CSS的浮动和清除
CSS浮动和清除 什么是浮动? 在实现效果上,让元素浮起来(飘起来),动起来(向左或向右移动) 浮动本质:就是将两个块元素同存一行. float 取值:主要是对浮动的方向进行控制 left:让元素向左 ...
- TIMAC 学习笔记(三)
本文主要内容参考 <Security on TI IEEE 802.15.4 Compliant RF Devices>.<Design Note DN108>.<IEE ...
- TweenMax动画库学习(四)
目录 TweenMax动画库学习(一) TweenMax动画库学习(二) TweenMax动画库学习(三) Tw ...
- asp.net导出Excel 按照预定格式,以及解决导出乱码
protected void ToExcel() { //新建一个Gridview,原因:避免当前窗口GridView外层没有直接跟form标签,从而避免“gridview1未包含在run='serv ...
- mouseenter 事件,固定右侧客服特效
不论鼠标指针穿过被选元素或其子元素,都会触发 mouseover 事件. 只有在鼠标指针穿过被选元素时,才会触发 mouseenter 事件. 当鼠标指针离开元素时,会发生 mouseleave 事件 ...
- gridview 字段没有绑定由于column visible= false
由于gridview column visible=false, 后面执行gridview databound()操作 不会将数据绑定到相关的单元格,其实这个时候我们希望绑定数据只是不显示而已. 可以 ...