3

Suppose I've got a function foo:Int => Try[Int] and I need to call it with retries. That is, I need to call it till it returns Success at most k times.

I am writing a function retry like that:

def retry(k: retries)(fun: Int => Try[Int]): Try[Int] = ???

I want retry to return either Success or the last Failure. How would you write it ?

Michael
  • 41,026
  • 70
  • 193
  • 341
  • Similar question: https://stackoverflow.com/questions/48074016/converting-any-method-to-retry-able-method-in-scala – Jasper-M Jan 25 '18 at 11:02

1 Answers1

5

This is the one I use, which is generic over any thunk returning T:

@tailrec
final def withRetry[T](retries: Int)(fn: => T): Try[T] = {
  Try(fn) match {
    case x: Success[T] => x
    case _ if retries > 1 => withRetry(retries - 1)(fn)
    case f => f
  }
}
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • Thanks. Can it be done without recursion ? Just curious. – Michael Jan 25 '18 at 10:20
  • @Michael I guess every tailrec recursion can be written via loop. – Sergii Lagutin Jan 25 '18 at 10:34
  • @Michael You definitely can, but it's a bit less elegant IMO – Yuval Itzchakov Jan 25 '18 at 10:45
  • @SergeyLagutin Yes. I meant writing w/o recursion _functionally_ (i.e. w/o mutable state). – Michael Jan 25 '18 at 11:35
  • @Michael Without mutable state is going to be harder. But the mutable state is all scoped at the method level and isn't visible outside, so it won't be that bad. – Yuval Itzchakov Jan 25 '18 at 11:54
  • Maybe you are right ... On the other hand, what if I need another function `WithRetry2` to return _all_ failures rather than only the last one ? I would like to reuse `WithRetry` to write `WithRetry2` instead of writing it from scratch. – Michael Jan 25 '18 at 14:06
  • @Michael You can write a recursive method which accumulates all errors and returns them, and have a specific `withRetrySingle` which returns the tail of the accumulator (or the head, depending on your insertion order). – Yuval Itzchakov Jan 25 '18 at 15:23
  • Thanks. I want to try using a lazy collection. – Michael Jan 25 '18 at 15:27
  • @YuvalItzchakov what does the `case f => f` handle? My understanding is `Try[T]` returns either `Success[T]` or `Failure[T]`. – billydh Aug 25 '19 at 23:41
  • @billydh It handles the exaution of the retry, and returning whatever was yielded by `f`, which is going to be a failure. Would be similar if we matched on `case f: Failure[_] => ..` – Yuval Itzchakov Sep 29 '22 at 15:25