简体   繁体   中英

Can't overload apply method in Scala Implicit class

I am writing a retry function with async and await

  def awaitRetry[T](times: Int)(block: => Future[T]): Future[T] = async {
    var i = 0
    var result: Try[T] = Failure(new RuntimeException("failure"))

    while (result.isFailure && i < times) {
      result = await { Try(block) } // can't compile
      i += 1
    }

    result.get
  }

The Scala compiler reports an error. Since Try doesn't have apply methods takes Future[T] . So I solved it using implicit classes

  implicit class TryCompanionOps(val t: Try.type) extends AnyVal {
    // can't use `apply`!
    def convertTriedFuture[T](f: => Future[T]): Future[Try[T]] = 
      f.map(value => Try(value))
  }  

  // now we can convert Future[T] into Future[Try[T]] e.g,
  await { Try.convertTriedFuture(block) }

My question is,
Why can't I use the name apply instead of convertTriedFuture ? It seems that the scala compiler doesn't allow overload only about apply methods in implicit classes .

Thanks.

Scala starts to look for implicit conversions, only when it can't find an existing method with the required signature. But in Try companion object there is already a suitable method: def apply[T](r: ⇒ T): Try[T] , so Scala infers T in apply as Future[Something] and doesn't check for implicit conversions.

Also, this implementation won't work:

def convertTriedFuture[T](f: => Future[T]): Future[Try[T]] = 
  f.map(value => Try(value))

If the Future is failed, map 's function isn't called, and the Exception is thrown from await , and async immediately results in a failed Future . So with this implementation the function doesn't actually retry.

You need something like:

def convertTriedFuture[T](f: => Future[T]): Future[Try[T]] =
  f.map(Success.apply).recover { case e => Failure(e) }

Also, I think, it might be cleaner to define this recovery method on Futures:

implicit class FutureAdditionalOps[T](f: Future[T]) {
  def recoverError: Future[Try[T]] =
    f.map(Success.apply).recover { case e => Failure(e) }
}

And then you can have

result = await { block.recoverError }

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