Still the newbie in Scala and I\'m now looking for a way to implement the following code on it:
@Override
public void store(InputStream source, String destin
I like the accepted solution, but suggest checking the exception is NonFatal:
// Returning T, throwing the exception on failure
@annotation.tailrec
def retry[T](n: Int)(fn: => T): T = {
Try { fn } match {
case Success(x) => x
case _ if n > 1 && NonFatal(e) => retry(n - 1)(fn)
case Failure(e) => throw e
}
}
You don't want to retry a control flow exception, and usually not for thread interrupts...
This project seems to provide some nice implementations for different retry mechanisms https://github.com/hipjim/scala-retry
// define the retry strategy
implicit val retryStrategy =
RetryStrategy.fixedBackOff(retryDuration = 1.seconds, maxAttempts = 2)
// pattern match the result
val r = Retry(1 / 1) match {
case Success(x) => x
case Failure(t) => log("I got 99 problems but you won't be one", t)
}
This solution is not optimized by compiler to tail recursion for some reason (who knows why?), but in case of rare retries would be an option:
def retry[T](n: Int)(f: => T): T = {
Try { f } recover {
case _ if n > 1 => retry(n - 1)(f)
} get
}
Usage:
val words: String = retry(3) {
whatDoesTheFoxSay()
}
End of the answer. Stop reading here
def reTry[T](n: Int)(f: => T): Try[T] = {
Try { f } recoverWith {
case _ if n > 1 => reTry(n - 1)(f)
}
}
Usage:
// previous usage section will be identical to:
val words: String = reTry(3) {
whatDoesTheFoxSay()
} get
// Try as a result:
val words: Try[String] = reTry(3) {
whatDoesTheFoxSay()
}
def retry[T](n: Int)(f: => Try[T]): Try[T] = {
f recoverWith {
case _ if n > 1 => reTry(n - 1)(f)
}
}
Usage:
// the first usage section will be identical to:
val words: String = retry(3) {
Try(whatDoesTheFoxSay())
} get
// if your function returns Try:
def tryAskingFox(): Try = Failure(new IllegalStateException)
val words: Try[String] = retry(3) {
tryAskingFox()
}
Recursion + first class functions by-name parameters == awesome.
def retry[T](n: Int)(fn: => T): T = {
try {
fn
} catch {
case e =>
if (n > 1) retry(n - 1)(fn)
else throw e
}
}
Usage is like this:
retry(3) {
// insert code that may fail here
}
Edit: slight variation inspired by @themel's answer. One fewer line of code :-)
def retry[T](n: Int)(fn: => T): T = {
try {
fn
} catch {
case e if n > 1 =>
retry(n - 1)(fn)
}
}
Edit Again: The recursion bothered me in that it added several calls to the stack trace. For some reason, the compiler couldn't optimize tail recursion in the catch handler. Tail recursion not in the catch handler, though, optimizes just fine :-)
@annotation.tailrec
def retry[T](n: Int)(fn: => T): T = {
val r = try { Some(fn) } catch { case e: Exception if n > 1 => None }
r match {
case Some(x) => x
case None => retry(n - 1)(fn)
}
}
Edit yet again: Apparently I'm going to make it a hobby to keep coming back and adding alternatives to this answer. Here's a tail-recursive version that's a bit more straightforward than using Option
, but using return
to short-circuit a function isn't idiomatic Scala.
@annotation.tailrec
def retry[T](n: Int)(fn: => T): T = {
try {
return fn
} catch {
case e if n > 1 => // ignore
}
retry(n - 1)(fn)
}
Scala 2.10 update. As is my hobby, I revisit this answer occasionally. Scala 2.10 as introduced Try, which provides a clean way of implementing retry in a tail-recursive way.
// Returning T, throwing the exception on failure
@annotation.tailrec
def retry[T](n: Int)(fn: => T): T = {
util.Try { fn } match {
case util.Success(x) => x
case _ if n > 1 => retry(n - 1)(fn)
case util.Failure(e) => throw e
}
}
// Returning a Try[T] wrapper
@annotation.tailrec
def retry[T](n: Int)(fn: => T): util.Try[T] = {
util.Try { fn } match {
case util.Failure(_) if n > 1 => retry(n - 1)(fn)
case fn => fn
}
}
Minor improvement to printout attempt x of N
// Returning T, throwing the exception on failure
@annotation.tailrec
final def retry[T](n: Int, name: String ="", attemptCount:Int = 1)(fn: => T): T = {
logger.info(s"retry count: attempt $attemptCount of $n ....... function: $name")
try {
val result = fn
logger.info(s"Succeeded: attempt $attemptCount of $n ....... function: $name")
result
} catch {
case e: Throwable =>
if (n < attemptCount) { Thread.sleep(5000 * attemptCount); retry(n, name, attemptCount+1)(fn) }
else throw e
}
}
You can express the idea in functional style using scala.util.control.Exception:
@annotation.tailrec
def retry[T](n: Int)(fn: => T): T =
Exception.allCatch.either(fn) match {
case Right(v) => v;
case Left(e) if (n <= 1) => throw e;
case _ => retry(n - 1)(fn);
}
As we can see, tail recursion can be used here.
This approach gives you the additional benefit that you can parametrize the catch container, so you can only retry a certain subset of exceptions, add finalizers etc. So the final version of retry
might look like:
/** Retry on any exception, no finalizers. */
def retry[T](n: Int)(fn: => T): T =
retry(Exception.allCatch[T], n)(fn);
/** Parametrized retry. */
@annotation.tailrec
def retry[T](theCatch: Exception.Catch[T], n: Int)(fn: => T): T =
theCatch.either(fn) match {
case Right(v) => v;
case Left(e) if (n <= 1) => throw e;
case _ => retry(theCatch, n - 1)(fn);
}
With this, you can do complex stuff like:
retry(Exception.allCatch andFinally { print("Finished.") }, 3) {
// your scode
}