Is there a functional/Scala way to call a function repeatedly until it succeeds, while reacting to failed attempts?
Let me illustrate with an example. Suppose I want to read an integer from standard-in, and retry if the user did not in fact enter an integer.
Given this function:
def read_int(): Either[String, Int] = {
  val str = scala.io.StdIn.readLine()
  try {
    Right(str.trim().toInt)
  } catch {
    case _: java.lang.NumberFormatException => Left(str)
  }
}
And this anonymous functions:
val ask_for_int = () => {
  println("Please enter an Int:")
  read_int()
}
val handle_not_int = (s: String) => {
  println("That was not an Int! You typed: " + s)
}
I would use them like this:
val num = retry_until_right(ask_for_int)(handle_not_int)
println(s"Thanks! You entered: $num")
My questions are:
retry_until_right already exist in Scala?Thanks!
*) Apart from the snake_case. I really like it.
def retry[L, R](f: => Either[L, R])(handler: L => Any): R = {
    val e = f
    e.fold(l => { handler(l); retry(f)(handler) }, identity)
}
                        I think the Try monad together with the Iterator.continually method is suitable for this general problem.  Of course, this answer could be adopted to use Either if you are so inclined:
def retry[T](op: => Try[T])(onWrong: Throwable => Any) = 
    Iterator.continually(op).flatMap { 
        case Success(t) => Some(t)
        case Failure(f) => onWrong(f); None 
    }.toSeq.head
Then you can do:
retry { Try(scala.io.StdIn.readLine.toInt) }{ _ => println("failed!") }
Or you can even hide the Try part of the implementation, and give onWrong a default value, and make it a second parameter instead of a curried function:
def retry[T](op: => T, onWrong: Throwable => Any = _ => ()) = 
    Iterator.continually(Try(op)).flatMap { 
        case Success(t) => Some(t)
        case Failure(f) => onWrong(f); None 
    }.toSeq.head
So then you can simply:
retry { scala.io.StdIn.readLine.toInt } { _ => println("failed") }
Or
retry { scala.io.StdIn.readLine.toInt }
                        Here's an alternative solution using scalaz.concurrent.Task:
import scalaz.concurrent.Task
def readInt: Task[Int] = {
  Task.delay(scala.io.StdIn.readLine().trim().toInt).handleWith {
    case e: java.lang.NumberFormatException =>
      Task.delay(println("Failure!")) flatMap (_ => readInt)
  }
}
And a retry wrapper (a bit less flexible):
def retry[A](f: Task[A])(onError: PartialFunction[Throwable, Task[_]]): Task[A] =
  f handleWith (onError andThen (_.flatMap(_ => retry(f)(onError))))
val rawReadInt: Task[Int] = Task.delay(scala.io.StdIn.readLine().trim().toInt)
val readInt: Task[Int] = retry(rawReadInt) {
  case e: java.lang.NumberFormatException => Task.delay(println("Failure!"))
}
scalaz.concurrent.Task[A] is a monadic structure that eventually returns a A. It uses a trampoline to (usually) avoid stack overflows. It also handles exceptions, and can either rethrow the exception, or represent the exception via \/ (scalaz's right-biased Either).
handleWith allows one to write a handler for a Throwable which was raised by the Task. The result of this handler is a new Task to run afterwards. In this case, we will just print out an error message, and call the original Task again using flatMap. Since Task is a trampolined construct, this should be safe.
Try this with readInt.run - this will run the task on the current thread, and eventually return the Int value passed in.
This was my first tail-recursive implementation:
@scala.annotation.tailrec
def retry_until_right[WRONG, RIGHT](generator: () => Either[WRONG, RIGHT])(on_wrong: WRONG => Any): RIGHT = {
  generator() match {
    case Right(right) =>
      right
    case Left(wrong) =>
      on_wrong(wrong)
      retry_until_right(generator)(on_wrong)
  }
}
But wanting to re-use existing libraries, I then switched to this approach using iterators:
def retry_until_right[WRONG, RIGHT](generator: => Either[WRONG, RIGHT])(on_wrong: WRONG => Any): RIGHT =
  Iterator.continually(generator).flatMap {
    case Left(value) =>
      on_wrong(value)
      None
    case Right(value) =>
      Some(value)
  }.toSeq.head
Which can be used like this:
val num = retry_until_right(ask_for_int()) { str =>
  println("Ivalid input: " + str)
}
println("Thanks! You entered: " + num)
However, it could be argued that hiding the Iterator from plain view inside the implementation can be inflexible. What if the developer wants to perform further operations on it? (mapping, etc.)
Instead, the operations that react on "wrong" values and finally select the first "right" one can be abstracted into an "extension" class specific for iterators of the Either[L,R] type, like this:
implicit class EitherIteratorExtensions[L, R](it: Iterator[Either[L, R]]) {
  def onLeft(callback: L => Any) =
    it.map {
      case left @ Left(value) =>
        callback(value)
        left
      case right => right
    }
  // take only Right elements
  def takeRight: Iterator[R] = 
    it.flatMap {
      case Left(_) =>
        None
      case Right(value) => Some(value)
    }
  // iterate and fetch the first Right element
  def firstRight: R = {
    takeRight.toSeq.head
  }
}
Now we can comfortably use the needed methods, in succinct code, while retaining control of the Iterator like this:
val num = Iterator.continually(ask_for_int()).onLeft(handle_not_int).firstRight
println("Thanks! You entered: " + num)
Though I'm happy with this approach, I still wonder if this is not part of an existing library already...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With