【问题标题】:Can't overload apply method in Scala Implicit class无法在 Scala 隐式类中重载应用方法
【发布时间】:2015-05-03 08:51:48
【问题描述】:

我正在用asyncawait 编写一个retry 函数

  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
  }

Scala 编译器报告错误。由于Try 没有应用方法,因此需要Future[T]。所以我使用 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) }

我的问题是,
为什么我不能使用名称apply 而不是convertTriedFuture?似乎 scala 编译器不允许 overload 只允许 implicit classes 中的 apply 方法。

谢谢。

【问题讨论】:

    标签: scala apply implicit


    【解决方案1】:

    Scala 仅在找不到具有所需签名的现有方法时才开始寻找隐式转换。但是在Try伴生对象中已经有一个合适的方法:def apply[T](r: ⇒ T): Try[T],所以Scala将apply中的T推断为Future[Something]并且不检查隐式转换。

    另外,这个实现也行不通:

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

    如果Future 失败,map 的函数不会被调用,并且await 会抛出异常,async 会立即导致Future 失败。所以有了这个实现,函数实际上并没有重试。

    你需要这样的东西:

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

    另外,我认为,在 Futures 上定义这种恢复方法可能更简洁:

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

    然后你可以拥有

    result = await { block.recoverError }
    

    【讨论】:

    • 对潜在性能问题的评论非常有帮助。谢谢科尔马。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-06
    • 1970-01-01
    相关资源
    最近更新 更多