-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0be7f05
commit 3d95425
Showing
10 changed files
with
10 additions
and
640 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,49 +1 @@ | ||
# Futures in Scala 2.12 (part 1) | ||
|
||
This is the first of several posts describing the evolution of `scala.concurrent.Future` in Scala `2.12.x`. | ||
|
||
## Missing canonical combinators: flatten | ||
|
||
Are you one of us Future-users who have grown tired of the old `flatMap(identity)` boilerplate for un-nesting `Future`s as in: | ||
|
||
~~~scala | ||
val future: Future[Future[X]] = ??? | ||
val flattenedFuture /*: Future[X] */ = future.flatMap(identity) | ||
~~~ | ||
|
||
Then I have some great news for you! | ||
Starting with Scala 2.12 `scala.concurrent.Future` will have a `flatten`-method with the following signature: | ||
|
||
~~~scala | ||
def flatten[S](implicit ev: T <:< Future[S]): Future[S] | ||
~~~ | ||
|
||
Allowing you to write: | ||
|
||
~~~scala | ||
val future: Future[Future[X]] = ??? | ||
val flattenedFuture /*: Future[X] */ = future.flatten | ||
~~~ | ||
|
||
### Benefits: | ||
|
||
1. Less to type | ||
2. Less to read | ||
3. Does not require any `ExecutionContext` | ||
|
||
**Bonus**: Doesn't allocate a function instance as `flatMap(identity)` does: | ||
|
||
~~~scala | ||
scala> def sameInstance[T](first: T => T, second: T => T) = first eq second | ||
sameInstance: [T](first: T => T, second: T => T)Boolean | ||
|
||
scala> sameInstance[Int](identity, identity) | ||
res0: Boolean = false | ||
~~~ | ||
|
||
[Here's the RSS feed of this blog](https://github.com/viktorklang/blog/commits/master.atom) and—as I love feedback—please [share your thoughts](https://github.com/viktorklang/blog/issues/3). | ||
|
||
[Click here](https://github.com/viktorklang/blog/blob/master/Futures-in-Scala-2.12-part-2.md) for the next part in this blog series. | ||
|
||
Cheers, | ||
√ | ||
This post has moved to [here](http://viktorklang.com/blog/Futures-in-Scala-2.12-part-1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,57 +1 @@ | ||
# Futures in Scala 2.12 (part 2) | ||
|
||
This is the second of several posts describing the evolution of `scala.concurrent.Future` in Scala `2.12.x`. | ||
For the previous post, [click here](https://github.com/viktorklang/blog/blob/master/Futures-in-Scala-2.12-part-1.md). | ||
|
||
## Missing canonical combinators: zipWith | ||
|
||
Being able to *join* two `Future`s together to produce a new `Future` | ||
which contains a `Tuple` of the results of both has been available for a while with the `zip` operation, and this looks something like this: | ||
|
||
~~~scala | ||
val future1: Future[String] = … | ||
val future2: Future[Int] = … | ||
val zippedFuture: Future[(String, Int)] = future1 zip future2 | ||
~~~ | ||
|
||
But what if we don't want a `Tuple`? What if we want to *combine* the results | ||
of the two `Future`s to create something else? | ||
|
||
This typically means having to combine `zip` and `map`: | ||
|
||
~~~scala | ||
/* The reason why we use a «PartialFunction literal» (yes, I made that up) | ||
* here is because otherwise the code has to use the arguably uglier _._1 and | ||
* _._2 syntax for `Tuple` value extraction. | ||
*/ | ||
val combinedFuture: Future[String] = | ||
future1 zip future2 map { case (string, int) => s"$string & $int" } | ||
~~~ | ||
|
||
Now, if you're like me and you dislike allocations, tuples and lack of genericity, you'll see that `zip` is a specialization of `x.zipWith(y)(Tuple2.apply)`! Well, perhaps it helps if we share the signature of said `zipWith`: | ||
|
||
~~~scala | ||
def zipWith[U, R](that: Future[U])(f: (T, U) => R)(implicit executor: ExecutionContext): Future[R] | ||
~~~ | ||
|
||
This allows us to express the example above as: | ||
|
||
~~~scala | ||
val combinedFuture: Future[String] = | ||
future1.zipWith(future2)((string, int) => s"$string & $int") | ||
~~~ | ||
|
||
### Benefits: | ||
|
||
1. Less to type | ||
2. Less to read | ||
3. Fewer allocations | ||
4. Fewer asynchronous steps | ||
5. More general than `zip` | ||
|
||
[Here's the RSS feed of this blog](https://github.com/viktorklang/blog/commits/master.atom) and—as I love feedback—please [share your thoughts](https://github.com/viktorklang/blog/issues/3). | ||
|
||
[Click here](https://github.com/viktorklang/blog/blob/master/Futures-in-Scala-2.12-part-3.md) for the next part in this blog series. | ||
|
||
Cheers, | ||
√ | ||
This post has moved to [here](http://viktorklang.com/blog/Futures-in-Scala-2.12-part-2) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,61 +1 @@ | ||
# Futures in Scala 2.12 (part 3) | ||
|
||
This is the third of several posts describing the evolution of `scala.concurrent.Future` in Scala `2.12.x`. | ||
For the previous post, [click here](https://github.com/viktorklang/blog/blob/master/Futures-in-Scala-2.12-part-2.md). | ||
|
||
## Missing canonical combinators: transform | ||
|
||
Now, if you're thinking "Hey Viktor, there's already a `transform`-method on `Future`!" then you're most definitely right. And I'll explain myself. | ||
|
||
When the old `transform`<sup>[1](#transformNote)</sup> was added, many moons ago, it was a means of mapping over successes and failures. However, the astute reader might notice that it is asymmetric: an exception thrown when mapping over the successful case will result in a failed `Future`, but there is no way to turn a failed case into a successful one. | ||
|
||
One way of looking at a `scala.concurrent.Future` is that it's an "`Eventually[Try[_]]`", in other words, a container which will eventually contain a `scala.util.Try` parameterized by some type. | ||
|
||
What if we had a way to *transform* `Future` more generically, and symmetrically? | ||
|
||
Perhaps its signature ought to look something like this: | ||
|
||
~~~scala | ||
def transform[S](f: Try[T] => Try[S])(implicit executor: ExecutionContext): Future[S] | ||
~~~ | ||
|
||
So for instance, that would mean that `Future.map` could be implemented as follows (and is!): | ||
|
||
~~~scala | ||
def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S] = transform(_.map(f)) | ||
~~~ | ||
|
||
And `Future.recover` is possible to implement as this (and is!): | ||
|
||
~~~scala | ||
def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Future[U] = | ||
transform { _ recover pf } | ||
~~~ | ||
|
||
It also makes it *dead simple* to *lift* a `Future[T]` to a `Future[Try[T]]`: | ||
|
||
~~~scala | ||
val someFuture: Future[String] = … | ||
val lifted/*: Future[Try[String]]*/ = someFuture.transform(Try(_)) | ||
~~~ | ||
|
||
Nice, right? | ||
|
||
|
||
### Benefits: | ||
|
||
1. Strictly more powerful / generic than the previous `transform`-method | ||
2. Straightforward to consume/transform a `Future`s `Try[_]` without having to use `onComplete` or unboxing `Future.value` | ||
3. For implementors of the `Future` trait, less methods to implement | ||
|
||
[Here's the RSS feed of this blog](https://github.com/viktorklang/blog/commits/master.atom) and—as I love feedback—please [share your thoughts](https://github.com/viktorklang/blog/issues/3). | ||
|
||
[Click here](https://github.com/viktorklang/blog/blob/master/Futures-in-Scala-2.12-part-4.md) for the next part in this blog series. | ||
|
||
Cheers, | ||
√ | ||
|
||
<a name="transformNote">1</a>: | ||
~~~scala | ||
def transform[S](s: T => S, f: Throwable => Throwable)(implicit executor: ExecutionContext): Future[S] | ||
~~~ | ||
This post has moved to [here](http://viktorklang.com/blog/Futures-in-Scala-2.12-part-3) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1 @@ | ||
# Futures in Scala 2.12 (part 4) | ||
|
||
This is the fourth of several posts describing the evolution of `scala.concurrent.Future` in Scala `2.12.x`. | ||
For the previous post, [click here](https://github.com/viktorklang/blog/blob/master/Futures-in-Scala-2.12-part-3.md). | ||
|
||
## Missing canonical combinators: transformWith | ||
|
||
As we saw in the previous post, `transform` provides a nice unification of both `map` and `recover` on `Future`, and I know what you're thinking now: "What about `flatMap` and `recoverWith`?" | ||
|
||
Say no more! `transformWith` is the answer, and has the following lovely signature: | ||
|
||
~~~scala | ||
def transformWith[S](f: Try[T] => Future[S])(implicit executor: ExecutionContext): Future[S] | ||
~~~ | ||
|
||
Remember to use the solution with the least power which will suffice for the task at hand, so prefer to use the `flatMap`s and the `recoverWith`s primarily, opting for the `transformWith` as required. | ||
|
||
And, before you say anything, yes, `flatMap` and `recoverWith` are implemented in terms of `transformWith` in Scala 2.12! | ||
|
||
EDIT: | ||
|
||
Here's `flatMap`: | ||
|
||
~~~scala | ||
def flatMap[S](f: T => Future[S])(implicit executor: ExecutionContext): Future[S] = transformWith { | ||
case Success(s) => f(s) | ||
case Failure(_) => this.asInstanceOf[Future[S]] //Safe cast to reuse current, failed, Future | ||
} | ||
~~~ | ||
|
||
And here's `recoverWith`: | ||
|
||
~~~scala | ||
def recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]])(implicit executor: ExecutionContext): Future[U] = | ||
transformWith { | ||
case Failure(t) => pf.applyOrElse(t, (_: Throwable) => this) //Pass along current failure if no match | ||
case Success(_) => this | ||
} | ||
~~~ | ||
|
||
### Benefits: | ||
|
||
1. Ultimate power, for when you require it | ||
|
||
[Here's the RSS feed of this blog](https://github.com/viktorklang/blog/commits/master.atom) and—as I love feedback—please [share your thoughts](https://github.com/viktorklang/blog/issues/3). | ||
|
||
To comment on the blog post itself, [click here](https://github.com/viktorklang/blog/pull/4/files) or [here](https://github.com/viktorklang/blog/pull/6/files) and comment on the PR. | ||
|
||
[Click here](https://github.com/viktorklang/blog/blob/master/Futures-in-Scala-2.12-part-5.md) for the next part in this blog series. | ||
|
||
Cheers, | ||
√ | ||
This post has moved to [here](http://viktorklang.com/blog/Futures-in-Scala-2.12-part-4) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,53 +1 @@ | ||
# Futures in Scala 2.12 (part 5) | ||
|
||
This is the fifth of several posts describing the evolution of `scala.concurrent.Future` in Scala `2.12.x`. | ||
For the previous post, [click here](https://github.com/viktorklang/blog/blob/master/Futures-in-Scala-2.12-part-4.md). | ||
|
||
## Deprecations: onSuccess and onFailure | ||
|
||
Since its inception and subsequent inclusion in the Scala Standard Library, `Future` has had 3 distinctly identifiable callback methods. `onComplete`, `onSuccess`, and `onFailure`. | ||
|
||
Now, you're perhaps asking yourself why it is that `onSuccess` and `onFailure` are special-cases of `onComplete`, operating on eiter side of success/failure? Well, at some point it was conceived as useful. | ||
|
||
"But…", I hear you say, "isn't `foreach` *just* a total version of `onSuccess`?" Well, yes it is. So let's use that instead, or `onComplete`! | ||
|
||
|
||
So, if you call `someFuture.onSuccess(somePartialFunction)` in Scala 2.12.x you'll get the following deprecation message: | ||
|
||
~~~txt | ||
use `foreach` or `onComplete` instead (keep in mind that they take total rather than partial functions) | ||
~~~ | ||
|
||
And then I hear you thinking "But, if `onSuccess` is the partial version of `foreach`, doesn't that mean that `onFailure` is the partial version of `failed.foreach`?" YES—exactly that! | ||
|
||
This is why `someFuture.onFailure(somePartialFunction)´ in Scala 2.12.x yields this deprecation message: | ||
|
||
~~~txt | ||
use `onComplete` or `failed.foreach` instead (keep in mind that they take total rather than partial functions) | ||
~~~ | ||
|
||
Worth keeping in mind, as with all the callbacks & `foreach`—there is no guaranteed order of execution of callbacks attached to the same `Future`. | ||
To illustrate: | ||
|
||
~~~scala | ||
val someFuture: Future[Missile] = … | ||
someFuture.foreach(_.neutralize(PERMANENTLY)) | ||
someFuture.foreach(_.launch(target)) | ||
//Could be executed in any order and not unlikely in parallel | ||
~~~ | ||
|
||
### Benefits: | ||
|
||
1. Promotion of for-comprehension compatible API instead of callbacks | ||
2. In the end there'll be fewer methods on Future, being less confusing as to what to use and when | ||
3. `onComplete` then remains as a performance-optimization of [`transform`](https://github.com/viktorklang/blog/blob/master/Futures-in-Scala-2.12-part-3.md), not having to create `Future`s to return. | ||
|
||
|
||
[Here's the RSS feed of this blog](https://github.com/viktorklang/blog/commits/master.atom) and—as I love feedback—please [share your thoughts](https://github.com/viktorklang/blog/issues/3). | ||
|
||
To comment on the blog post itself, [click here](https://github.com/viktorklang/blog/pull/7/files) and comment on the PR. | ||
|
||
[Click here](https://github.com/viktorklang/blog/blob/master/Futures-in-Scala-2.12-part-6.md) for the next part in this blog series. | ||
|
||
Cheers, | ||
√ | ||
This post has moved to [here](http://viktorklang.com/blog/Futures-in-Scala-2.12-part-5) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,87 +1 @@ | ||
# Futures in Scala 2.12 (part 6) | ||
|
||
This is the sixth of several posts describing the evolution of `scala.concurrent.Future` in Scala `2.12.x`. | ||
For the previous post, [click here](https://github.com/viktorklang/blog/blob/master/Futures-in-Scala-2.12-part-5.md). | ||
|
||
## Missing utilities: unit & never | ||
|
||
Something I've always felt was missing is having a «zero» for `Future`—or more frequently called a «default instance» of a `Future`. | ||
|
||
What's *nice* about having this is that, technically, `Future.apply[T](logic: => T)(implicit ec: ExecutionContext)` could be viewed as, and implemented like: | ||
|
||
~~~scala | ||
def apply[T](logic: => T)(implicit ec: ExecutionContext): Future[T] = | ||
unit.map(_ => logic) | ||
~~~ | ||
|
||
Q: Where is it useful? | ||
A: Anywhere you have `Future.successful(())` | ||
|
||
Example: | ||
|
||
~~~scala | ||
//Imagine we don't want to try to store nulls or empty strings | ||
//Returns a Future[Unit] which will be completed once the operation has completed | ||
def storeInDB(s: String): Future[Unit] = s match { | ||
case null | "" => Future.unit | ||
case other => db.store(s) | ||
} | ||
|
||
val f: Future[String] = … | ||
val f2 = f flatMap storeInDB | ||
~~~ | ||
|
||
Another important scenario, which wasn't really readily solvable unless you were comfortable with implementing it yourself was a `Future` which would never complete. | ||
|
||
Now, the naïve implemention of said `Future` would look something like: | ||
|
||
~~~scala | ||
val neverCompletingFuture: Future[Nothing] = Promise[Nothing].future | ||
~~~ | ||
|
||
Take a few seconds to think about the following question: In what ways would that solution be undesirable? | ||
|
||
… | ||
|
||
… | ||
|
||
… | ||
|
||
… | ||
|
||
Hint: What happens with logic which gets added to `neverCompletingFuture`? | ||
Example: | ||
|
||
~~~scala | ||
val fooFuture = neverCompletingFuture.map(_ => "foo") | ||
|
||
//or | ||
|
||
neverCompletingFuture.onComplete(println) | ||
~~~ | ||
|
||
Well, what happens is that the logic is *packaged* and added to the `neverCompletingFuture` instance in order to be executed when `neverCompletingFuture` completes (which is never), and now we have a hard to spot invisible memory leak! | ||
|
||
So, in order to support the case when you want to be able to represent a `Future` which never completes, but also doesn't leak memory when used as a plain `Future`, use `Future.never`. | ||
|
||
An example use-case might be when you need to pass in a Future which should not affect the outcome, as in: | ||
|
||
~~~scala | ||
val someImportantFuture: Future[String] = … | ||
val someLessImportantFuture: Future[String] = if (someCondition) Future.never else Future.successful("pigdog") | ||
val first = Future.firstCompletedOf(someImportantFuture, someLessImportantFuture) // Will always pick someImportantFuture if someCondition is true | ||
~~~ | ||
|
||
### Benefits: | ||
|
||
1. Future.unit is a «zero» instance which is cached | ||
2. Future.never removes the risk of memory leaks when used as a `Future` instance which never completes | ||
|
||
[Here's the RSS feed of this blog](https://github.com/viktorklang/blog/commits/master.atom) and—as I love feedback—please [share your thoughts](https://github.com/viktorklang/blog/issues/3). | ||
|
||
To comment on the blog post itself, [click here](https://github.com/viktorklang/blog/pull/8/files). | ||
|
||
[Click here](https://github.com/viktorklang/blog/blob/master/Futures-in-Scala-2.12-part-7.md) for the next part in this blog series. | ||
|
||
Cheers, | ||
√ | ||
This post has moved to [here](http://viktorklang.com/blog/Futures-in-Scala-2.12-part-6) |
Oops, something went wrong.