Why do I need to use andThen in order to pattern match Futures?
Asked Answered
L

2

5

I found out that in order to pattern match Future fur Success/Failure, I need to use andThen (or onComplete, onSuccess...) and cannot use map. Why is that?

What I wanted to do (simplified, I am matching for Success and so on as well):

val f1 = Future(throw new Exception("Oops"))
f1 map { case Failure(e) => ??? }

Gives:

error: constructor cannot be instantiated to expected type;
 found   : scala.util.Failure[T]
 required: Nothing
       f1 map { case Failure(e) => ??? }

What I ended up doing:

val f1 = Future(throw new Exception("Oops"))
f1 andThen { case Failure(e) => ??? }

I would like to understand why map cannot be used here.

Liles answered 18/5, 2017 at 11:33 Comment(0)
C
5

The answer is in the signature of map: it takes a A => B and returns a Future[B]. If you will, you can look at a Future as follows:

type Future[A] = Async[Either[Throwable, A]]

Future#map, Future#flatMap and Future.apply view this "stack" of types as a single big thing with a hole (Future is basically a special cased monad transformer). When you map/flatMap on a Future, you are only operating on the inner A.

Connel answered 18/5, 2017 at 11:38 Comment(4)
So you mean that I would be only operating on the Exception itself and the rest (Future and Future#Failure) is "flatten", correct?Liles
The other way around: with map you only operate on A. To be able to act on the Exception you need other operators like recover or transform.Connel
Thanks a lot for the clarification!Liles
This is really helpful answer for slightly touching the "Monad Transformer" concept.Nealy
H
2

Because the type signature isn't correct. When you want to map over a Future[A], you need to provide a function taking an A and producing a B, which isn't what you seem to be doing. What you're looking for is recover:

f1 recover { case e => // e is already a `Throwable` here ??? }
Hluchy answered 18/5, 2017 at 11:40 Comment(4)
I'm aware of recover as well. Does it makes sense (i.e. not a bad practice) to use andThen to handle both Success and Failure, instead of map and then recover?Liles
@BobDem I'd say map and recover are more idiomatic. Note that comment on andThen: "Note that if one of the chained andThen callbacks throwsan exception, that exception is not propagated to the subsequent andThen callbacks. Instead, the subsequent andThen callbacks are given the original value of this future."Hluchy
That's a very clear drawback, even though it looks much cleaner (to me) and I'd have loved to use it for everything. Is it so then that it's general practice to wrap Futures with map to operate on success and recover for failures?Liles
@BobDem Yes, it is a general practice I use in most places where I use Future.Hluchy

© 2022 - 2024 — McMap. All rights reserved.