Slide 1

Slide 1 text

futures &promises in Scala 2.10 PHILIPP HALLER HEATHER MILLER

Slide 2

Slide 2 text

Futures/Promises Agenda Execution Ctxs tRY

Slide 3

Slide 3 text

common theme:

Slide 4

Slide 4 text

common theme: Pipelining

Slide 5

Slide 5 text

future &promise scala.concurrent.

Slide 6

Slide 6 text

First, some Motivation

Slide 7

Slide 7 text

1 Several important libraries have their own future/promise implementation

Slide 8

Slide 8 text

1 Several important libraries have their own future/promise implementation java.util.concurrent. scala.actors. com.twitter.util. akka.dispatch. scalaz.concurrent. net.liftweb.actor. FUTURE FUTURE FUTURE FUTURE PROMISE LAFUTURE

Slide 9

Slide 9 text

This makes it clear that...

Slide 10

Slide 10 text

This makes it clear that... futures are an important, powerful abstraction

Slide 11

Slide 11 text

This makes it clear that... futures are an important, powerful abstraction there’s fragmentation in the scala ecosystem no hope of interop!

Slide 12

Slide 12 text

Furthermore...

Slide 13

Slide 13 text

Furthermore... Java futures neither efficient nor composable 2

Slide 14

Slide 14 text

Furthermore... Java futures neither efficient nor composable 2 we could make futures more powerful, by taking advantage of scala’s features 3

Slide 15

Slide 15 text

can be thought of as a single concurrency abstraction Futures&Promises Future promise

Slide 16

Slide 16 text

can be thought of as a single concurrency abstraction Futures&Promises Future READ-MANY promise write-once

Slide 17

Slide 17 text

can be thought of as a single concurrency abstraction Futures&Promises Future READ-MANY promise write-once Start async computation ✔ important ops Assign result value ✔ Wait for result ✔ Obtain associated future object ✔

Slide 18

Slide 18 text

a promise p of type Promise[T] can be completed in two ways... Success&Failure val result: T = ... p.success(result) Success val exc = new Exception(“something went wrong”) p.failure(exc) Failure

Slide 19

Slide 19 text

Future Promise Future with value Green Red thread waiting on the result of another thread meaningful work java.util.concurrent.future

Slide 20

Slide 20 text

java.util.concurrent.future Future Promise Future with value Green Red thread waiting on the result of another thread meaningful work

Slide 21

Slide 21 text

what we’d like to do instead Future Promise Future with value Green Red thread waiting on the result of another thread meaningful work

Slide 22

Slide 22 text

Async&NonBlocking

Slide 23

Slide 23 text

Async&NonBlocking goal: Do not block current thread while waiting for result of future

Slide 24

Slide 24 text

Async&NonBlocking goal: Do not block current thread while waiting for result of future Callbacks Register callback which is invoked (asynchronously) when future is completed Async computations never block (except for managed blocking)

Slide 25

Slide 25 text

Async&NonBlocking goal: Do not block current thread while waiting for result of future Callbacks Register callback which is invoked (asynchronously) when future is completed Async computations never block (except for managed blocking) user doesn’t have to explicitly manage callbacks. higher-order functions instead!

Slide 26

Slide 26 text

Futures&Promises Thread1 Thread2 Thread3 example

Slide 27

Slide 27 text

Futures&Promises Promise val p = Promise[Int]() // Thread 1 Thread1 Thread2 Thread3 (create promise) example

Slide 28

Slide 28 text

Futures&Promises Promise Future val p = Promise[Int]() // Thread 1 val f = p.future // Thread 1 Thread1 Thread2 Thread3 (create promise) (get reference to future) example

Slide 29

Slide 29 text

Futures&Promises Promise Future val p = Promise[Int]() // Thread 1 val f = p.future // Thread 1 f onSuccess { // Thread 2 case x: Int => println(“Successful!”) } Thread1 Thread2 Thread3 onSuccess callback (create promise) (get reference to future) (register callback) example

Slide 30

Slide 30 text

Futures&Promises Promise Future val p = Promise[Int]() // Thread 1 val f = p.future // Thread 1 f onSuccess { // Thread 2 case x: Int => println(“Successful!”) } Thread1 Thread2 Thread3 onSuccess callback p.success(42) // Thread 1 42 42 (create promise) (get reference to future) (register callback) (write to promise) example

Slide 31

Slide 31 text

Futures&Promises Promise Future val p = Promise[Int]() // Thread 1 val f = p.future // Thread 1 f onSuccess { // Thread 2 case x: Int => println(“Successful!”) } Thread1 Thread2 Thread3 onSuccess callback p.success(42) // Thread 1 42 42 Successful! Console (create promise) (get reference to future) (register callback) (write to promise) (execute callback) // Thread example note: onSuccess callback executed even if f has already been completed at time of registration

Slide 32

Slide 32 text

Combinators val purchase: Future[Int] = rateQuote map { quote => connection.buy(amount, quote) } val postBySmith: Future[Post] = post.filter(_.author == “Smith”) Composability thru higher-order funcs standard monadic combinators map[S](f: T => S): Future[S] filter[S](pred: T => Boolean): Future[S]

Slide 33

Slide 33 text

Combinators val purchase: Future[Int] = rateQuote map { quote => connection.buy(amount, quote) } val postBySmith: Future[Post] = post.filter(_.author == “Smith”) Composability thru higher-order funcs standard monadic combinators map[S](f: T => S): Future[S] filter[S](pred: T => Boolean): Future[S] If filter fails: postBySmith completed with NoSuchElementException If map fails: purchase is completed with unhandled exception

Slide 34

Slide 34 text

Combinators Additional future-specific higher- order functions have been introduced def fallbackTo(that: Future[T]): Future[T] def firstCompletedOf(futures: Seq[Future[T]]): Future[T] def andThen[U](pf: PartialFunction[...]): Future[T]

Slide 35

Slide 35 text

Combinators Additional future-specific higher- order functions have been introduced def fallbackTo(that: Future[T]): Future[T] def firstCompletedOf(futures: Seq[Future[T]]): Future[T] def andThen[U](pf: PartialFunction[...]): Future[T] ”falls back” to that future in case of failure returns a future completed with result of first completed future allows one to define a sequential execution over a chain of futures

Slide 36

Slide 36 text

context Execution scala.concurrent.

Slide 37

Slide 37 text

are needed by: Threadpools... futures Actors parallel collections for executing callbacks and function arguments for executing message handlers, scheduled tasks, etc. for executing data-parallel operations

Slide 38

Slide 38 text

contexts Execution Scala 2.10 introduces

Slide 39

Slide 39 text

contexts Execution Scala 2.10 introduces provide global threadpool as platform service to be shared by all parallel frameworks Goal

Slide 40

Slide 40 text

contexts Execution Scala 2.10 introduces provide global threadpool as platform service to be shared by all parallel frameworks Goal scala.concurrent package provides global ExecutionContext Default ExecutionContext backed by the most recent fork join pool (collaboration with Doug Lea, SUNY Oswego)

Slide 41

Slide 41 text

Implicit Execution Ctxs def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S] def onSuccess[U](pf: PartialFunction[T, U]) (implicit executor: ExecutionContext): Unit By default, asynchronous computations are executed on the global ExecutionContext which is provided implicitly. Implicit parameters make it possible to override the default ExecutionContext: implicit val context: ExecutionContext = customExecutionContext val fut2 = fut1.filter(pred) .map(fun)

Slide 42

Slide 42 text

Implicit Execution Ctxs def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S] def onSuccess[U](pf: PartialFunction[T, U]) (implicit executor: ExecutionContext): Unit By default, asynchronous computations are executed on the global ExecutionContext which is provided implicitly. Implicit parameters make it possible to override the default ExecutionContext: implicit val context: ExecutionContext = customExecutionContext val fut2 = fut1.filter(pred) .map(fun) implicit ExecutionContexts allow sharing ecs between frameworks Enables flexible selection of execution policy

Slide 43

Slide 43 text

Future the implementation def map[S](f: T => S): Future[S] = { val p = Promise[S]() onComplete { case result => try { result match { case Success(r) => p success f(r) case Failure(t) => p failure t } } catch { case t: Throwable => p failure t } } p.future } Many operations implemented in terms of promises simplified example

Slide 44

Slide 44 text

Future the implementation REAL def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S] = { val p = Promise[S]() onComplete { case result => try { result match { case Success(r) => p success f(r) case f: Failure[_] => p complete f.asInstanceOf[Failure[S]] } } catch { case NonFatal(t) => p failure t } } p.future } The real implementation (a) adds an implicit ExecutionContext, (b) avoids extra object creations, and (c) catches only non-fatal exceptions:

Slide 45

Slide 45 text

Promise the implementation Promise is the work horse of the futures implementation def complete(result: Try[T]): this.type = if (tryComplete(result)) this else throw new IllegalStateException("Promise already completed.") A Promise[T] can be in one of two states: COMPLETED PENDING No result has been written to the promise State represented using a list of callbacks (initially empty) The promise has been assigned a successful result or exce. State represented using an instance of Try[T] Invoking Promise.complete triggers a transition from state Pending to Completed A Promise can be completed at most once:

Slide 46

Slide 46 text

def tryComplete(value: Try[T]): Boolean = { val resolved = resolveTry(value) (try { @tailrec def tryComplete(v: Try[T]): List[CallbackRunnable[T]] = { getState match { case raw: List[_] => val cur = raw.asInstanceOf[List[CallbackRunnable[T]]] if (updateState(cur, v)) cur else tryComplete(v) case _ => null } } tryComplete(resolved) } finally { synchronized { notifyAll() } // Notify any blockers }) match { case null => false case rs if rs.isEmpty => true case rs => rs.foreach(_.executeWithValue(resolved)); true } } Completing a Promise

Slide 47

Slide 47 text

the awkard squad abstract class AbstractPromise { private volatile Object _ref; final static long _refoffset; static { try { _refoffset = Unsafe.instance.objectFieldOffset( AbstractPromise.class.getDeclaredField("_ref")); } catch (Throwable t) { throw new ExceptionInInitializerError(t); } } protected boolean updateState(Object oldState, Object newState) { return Unsafe.instance.compareAndSwapObject(this, _refoffset, oldState, newState); } protected final Object getState() { return _ref; } }

Slide 48

Slide 48 text

Integrating Futures Actors & Futures are results of asynchronous message sends when a response is expected Fu wr val response: Future[Any] = socialGraph ? getFriends(user) Implementing synchronous send (untyped): def syncSend(to: ActorRef, msg: Any, timeout: Duration): Any = { val fut = to ? msg Await.result(fut, timeout) } Recovering types val friendsFut: Future[Seq[Friend]] = response.mapTo[Seq[Friend]]

Slide 49

Slide 49 text

Integrating Futures Actors & Futures are results of asynchronous message sends when a response is expected Fu wr val response: Future[Any] = socialGraph ? getFriends(user) Implementing synchronous send (untyped): def syncSend(to: ActorRef, msg: Any, timeout: Duration): Any = { val fut = to ? msg Await.result(fut, timeout) } Recovering types val friendsFut: Future[Seq[Friend]] = response.mapTo[Seq[Friend]] Future of Friends is either completed with a successful result or with a wrapped exception if response times out or is not of type Seq[Friend]

Slide 50

Slide 50 text

TRY scala.util.

Slide 51

Slide 51 text

And now onto something completely different.

Slide 52

Slide 52 text

And now onto something completely different. not concurrent, not asynchronous

Slide 53

Slide 53 text

Try is a simple data container Composable ✔ Combinators for exceptions ✔ Great for monadic-style exception handling. Divorcing exception handling from the stack.

Slide 54

Slide 54 text

Try is a simple data container sealed abstract class Try[+T] final case class Success[+T](value: T) extends Try[T] final case class Failure[+T](val exception: Throwable) extends Try[T]

Slide 55

Slide 55 text

Try is a simple data container sealed abstract class Try[+T] final case class Success[+T](value: T) extends Try[T] final case class Failure[+T](val exception: Throwable) extends Try[T]

Slide 56

Slide 56 text

Try is a simple data container sealed abstract class Try[+T] final case class Success[+T](value: T) extends Try[T] final case class Failure[+T](val exception: Throwable) extends Try[T]

Slide 57

Slide 57 text

Try methods on Basic Ops def get: T get Success Failure Returns value stored within Success Throws exception stored within Failure

Slide 58

Slide 58 text

Try methods on def getOrElse[U >: T](default: => U) Success Failure Returns value stored within Success Returns the given default argument if this is a Failure getOrElse Basic Ops

Slide 59

Slide 59 text

Try methods on def orElse[U >: T](default: => Try[U]) orElse Success Failure Returns this Try if this is a Success Returns the given default argument if this is a Failure Basic Ops

Slide 60

Slide 60 text

Try methods on def map[U](f: T => U): Try[U] map Success Failure Applies the function f to the value from Success Returns this if this is a Failure monadic Ops

Slide 61

Slide 61 text

Try methods on def flatMap[U](f: T => Try[U]): Try[U] flatMap Success Failure Applies the function f to the value from Success Returns this if this is a Failure monadic Ops

Slide 62

Slide 62 text

Try methods on def filter(p: T => Boolean): Try[T] filter Success Failure Converts this to a Failure if predicate p not satisfied. Returns this if this is a Failure monadic Ops

Slide 63

Slide 63 text

Try methods on def recoverWith[U >: T](f: PartialFunction[Throwable, Try[U]]): Try[U] recoverWith Success Failure Returns this if this is a Success Applies function f if this is a Failure. (flatMap on exptn) exception-specific Ops

Slide 64

Slide 64 text

Try methods on def recover[U >: T](f: PartialFunction[Throwable, U]): Try[U] recover Success Failure exception-specific Ops Returns this if this is a Success Applies function f if this is a Failure. (like map on exptn)

Slide 65

Slide 65 text

Try methods on def transform[U](s: T => Try[U], f: Throwable => Try[U]): Try[U] transform Success Failure Completes this Try by applying function f if this is a Success Completes this Try by applying function f if this is a Failure exception-specific Ops

Slide 66

Slide 66 text

TO BUILD USING THESE Pipelines

Slide 67

Slide 67 text

TO BUILD USING THESE Pipelines Remember: not concurrent, not asynchronous

Slide 68

Slide 68 text

Try Simple pipelining on Example 1 case class Account(acctNum: Int, balance: Double, interestRate: Double) val withdrawal = 1500 val adjustment = 0.4 val in = Try(getAcct) val withdrawalResult = in map { (x: Account) => Account(x.acctNum, x.balance - withdrawal, x.interestRate) } filter { (x: Account) => x.balance > 12000 // acct in good standing } map { (x: Account) => Account(x.acctNum, x.balance, x.interestRate + adjustment) Try(updateAcct) }

Slide 69

Slide 69 text

Try Simple pipelining on Example 1 case class Account(acctNum: Int, balance: Double, interestRate: Double) val withdrawal = 1500 val adjustment = 0.4 val in = Try(getAcct) val withdrawalResult = in map { (x: Account) => Account(x.acctNum, x.balance - withdrawal, x.interestRate) } filter { (x: Account) => x.balance > 12000 // acct in good standing } map { (x: Account) => Account(x.acctNum, x.balance, x.interestRate + adjustment) Try(updateAcct) } GETACCT MIGHT FAIL

Slide 70

Slide 70 text

Try Simple pipelining on Example 1 case class Account(acctNum: Int, balance: Double, interestRate: Double) val withdrawal = 1500 val adjustment = 0.4 val in = Try(getAcct) val withdrawalResult = in map { (x: Account) => Account(x.acctNum, x.balance - withdrawal, x.interestRate) } filter { (x: Account) => x.balance > 12000 // acct in good standing } map { (x: Account) => Account(x.acctNum, x.balance, x.interestRate + adjustment) Try(updateAcct) } possible arithmetic exception

Slide 71

Slide 71 text

Try Simple pipelining on Example 1 case class Account(acctNum: Int, balance: Double, interestRate: Double) val withdrawal = 1500 val adjustment = 0.4 val in = Try(getAcct) val withdrawalResult = in map { (x: Account) => Account(x.acctNum, x.balance - withdrawal, x.interestRate) } filter { (x: Account) => x.balance > 12000 // acct in good standing } map { (x: Account) => Account(x.acctNum, x.balance, x.interestRate + adjustment) Try(updateAcct) } Predicate might not be satisfied

Slide 72

Slide 72 text

Try Simple pipelining on Example 1 case class Account(acctNum: Int, balance: Double, interestRate: Double) val withdrawal = 1500 val adjustment = 0.4 val in = Try(getAcct) val withdrawalResult = in map { (x: Account) => Account(x.acctNum, x.balance - withdrawal, x.interestRate) } filter { (x: Account) => x.balance > 12000 // acct in good standing } map { (x: Account) => Account(x.acctNum, x.balance, x.interestRate + adjustment) Try(updateAcct) } possible arithmetic exception

Slide 73

Slide 73 text

Try Simple pipelining on Example 1 case class Account(acctNum: Int, balance: Double, interestRate: Double) val withdrawal = 1500 val adjustment = 0.4 val in = Try(getAcct) val withdrawalResult = in map { (x: Account) => Account(x.acctNum, x.balance - withdrawal, x.interestRate) } filter { (x: Account) => x.balance > 12000 // acct in good standing } map { (x: Account) => Account(x.acctNum, x.balance, x.interestRate + adjustment) Try(updateAcct) } UPDATEACCT MIGHT FAIL

Slide 74

Slide 74 text

Try Simple pipelining on Example 1 case class Account(acctNum: Int, balance: Double, interestRate: Double) val withdrawal = 1500 val adjustment = 0.4 val in = Try(getAcct) val withdrawalResult = in map { (x: Account) => Account(x.acctNum, x.balance - withdrawal, x.interestRate) } filter { (x: Account) => x.balance > 12000 // acct in good standing } map { (x: Account) => Account(x.acctNum, x.balance, x.interestRate + adjustment) Try(updateAcct) } Eliminates nested try blocks but how can we handle these failures?

Slide 75

Slide 75 text

Try Simple pipelining on Example 2 ...by using recoverWith, recover, or orElse case class Tweet(from: String, retweets: Int) val importantTweets = Try { server.getTweetList } orElse { cachedTweetList } map { twts => val avgRetweet = twts.map(_.retweets).reduce(_ + _) / twts.length (twts, avgRetweet) } filter { case (twts, avgRetweet) => twts.map(_.retweets).exists(_ > avgRetweet) } map { case (twts, avgRetweet) => twts.filter(_.retweets > avgRetweet) } recover { case nose: NoSuchElementException => // handle individually case usop: UnsupportedOperationException => // handle individually case other => // handle individually }

Slide 76

Slide 76 text

Try&Futures combining case class Friend(name: String, age: String) val avgAge = Promise[Int]() val fut = future { // query a social network... List(Friend("Zoe", "25"), Friend("Jean", "27"), Friend("Paul", "3O")) } fut onComplete { tr => // compute average age of friends val result = tr map { friends => friends.map(_.age.toInt).reduce(_ + _) / friends.length } avgAge complete result } avgAge.future onComplete println

Slide 77

Slide 77 text

Credits PHILIPP HALLER ALEX PROKOPEC VOJIN JOVANOVIC VIKTOR KLANG MARIUS ERIKSEN HEATHER MILLER ROLAND KUHN DOUG LEA TYPESAFE TYPESAFE EPFL EPFL EPFL TYPESAFE SUNY TWITTER HAVOC PENNINGTON TYPESAFE

Slide 78

Slide 78 text

questions ? http://docs.scala-lang.org/sips/pending/futures-promises.html