简体   繁体   中英

having trouble composing Scala Future from multiple Futures of different types

I have two functions: one returns a Future[Thing Or Exception] and another that returns Future[Boolean . I want a function that calls both and returns Future[Thing Or Exception] . If the boolean function returns false I want to return an exception, else the return of the other function.

I have code like this but a) I hate the cast and b) when run on the "boolean gets true" path I get this error when I eventually Await.result on the return in my test code: "Promise$DefaultPromise cannot be cast to org.scalatic.Or" .

def thingFuture: Future[Thing Or Exception]
def boolFuture: Future[Boolean]

def combineFutures: Future[Thing Or Exception] = {
   val result = boolFuture.map {x =>
     x match {
      case true => thingFuture
      case false => Exception
     }
   }
   // without the cast compiler says result is of type Future[Object]
   result.asInstanceOf[Future[Thing Or Exception]]
}

I've also tried this but it gets the same Promise error on the success path

def combineFutures: Future[Thing Or Exception] = {
   val result = boolFuture.map {x =>
     x match {
       case true => thingFuture.map { y =>
         y match {
           case Good(thing) => thing
           case Bad(exception) => exception
         }
       case false => Exception
     }
   }
}

Can anyone tell me how to compose two futures with different return types? Thanks!

Every future can be completed with failed state in case exception has occurred, so you can simply return thingFuture in the "happy path" and throw an exception in case boolean is false. This will return a Future.failed with the underlying exception.

val result = boolFuture.flatMap {x =>
  x match {
    case true => thingFuture
    case false => throw new Exception("whatever")
  }
}

Note the flatMap instead of map . Because we map the underlying value of one future into a yet another future, by using simple map we would wind up with Future[Future[Thing]] .

Also note that instead of throwing an exception, you could also return a Future.failed(throw new Exception("whatever")) and the result would be the same - in both case you get a failed future.

EDIT: I just realized Or comes from scalactic, which I never used, but the philosophy remains the same. You need to flatMap your boolean future and your ThingOrException future in order to wind up with Future[ThingOrException] . If you ever find yourself in a situation where you need to flatMap a Future, but one of the case clauses returns an ordinary value (eg in case of true return Future[Thing] , in case of false return just Exception ) then you can wrap the ordinary value into a future. This way all branches return a future and flatMap will work correctly. For example:

val someOtherFuture = Future(43)
val someOrdinaryValue = 44

Future(someInteger).flatMap {
  case 42 => someOtherFuture
  case _  => Future(someOrdinaryValue)
}

In order to simplify things for the runtime machinery a bit, you can also write Future.successful(someOrdinaryValue) in which case no background computation is started.

As far as I can tell from Scalatic documentation, you can get an instance of Right Or Left by either Good(Right) or Bad(Left) .

That means the composition can potentially look like this:

boolFuture.flatMap(b => if (b) thingFuture else Future.successful(Bad(new Exception())))

The types should unify to Future[Or[Thing, Exception]]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM