2015-05-03 1 views
0

Я пишу retry функции с async и awaitне перегружать применять метод в Scala неявного классе

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 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 не разрешает перегруз только около apply методов в неявных классов.

Спасибо.

ответ

2

Scala начинает искать неявные преобразования, только когда он не может найти существующий метод с требуемой сигнатурой. Но в Try сопутствующий объект уже есть подходящий метод: def apply[T](r: ⇒ T): Try[T], поэтому Scala сообщает T в apply как Future[Something] и не проверяет наличие неявных преобразований.

Кроме того, эта реализация не будет работать:

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

Если Future неудачен, map «s функция не вызывается, а исключение из await и async сразу приводит к сбою Future. Таким образом, с этой реализацией функция на самом деле не повторяет попытку.

Вам нужно что-то вроде:

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

Кроме того, я думаю, это может быть чище, чтобы определить этот метод восстановления на фьючерсы:

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 } 
+0

Комментарий к потенциальной проблеме производительности очень полезен. Спасибо, Кольмар. – 1ambda