7

I am trying to create a try clause analogue which repeats code block if exception occurred inside this code block.

def retry(attempts: Int)(func: Unit => Unit) {

  var attempt = 0
  while (attempt < attempts) {
    attempt += 1
    try {
      func()
    } catch {
      case _: Throwable =>
    }
  }
  throw new Exception()
}

I expect that it can be used like this

retry(10) { // I would like to pass this block as function
    val res = someNotReliableOp(); // <- exception may occur here
    print(res)
}

But it doesn't work:

Operations.scala:27: error: type mismatch;
 found   : Unit
 required: Unit => Unit
    print(res)
         ^
one error found

What is the most concise way to pass custom block to my function?

rae1
  • 6,066
  • 4
  • 27
  • 48
tmporaries
  • 1,523
  • 8
  • 25
  • 39
  • 1
    you dont want a function that returns a unit, you want a unit code block `(func: Unit => Unit)` => `(func: Unit)` – aepurniet Jan 16 '14 at 18:38
  • You do realize that even if you make the changes suggested in the answer that your code will still execute N times regardless of if an exception is thrown or not? – cmbaxter Jan 16 '14 at 18:54
  • 1
    @aepurniet That wouldn't work, because Scala would evaluate the func and then pass it's result (which we already know is `Unit`). The next time `func` is called it will do nothing, since it's a `Unit`. You need to pass the parameter with a call by name: `=> Unit` – Akos Krivachy Jan 16 '14 at 18:54
  • @cmbaxter of course I missed return after func() call. – tmporaries Jan 16 '14 at 18:56

2 Answers2

9

You just need to change your method definition a tiny bit:

def retry(attempts: Int)(func: => Unit)

Unit => Unit means: a function that takes a parameter with a type of Unit and evaluates to Unit.

=> Unit means: a function that takes no parameters and evaluates to Unit. This is called call by name.

Akos Krivachy
  • 4,915
  • 21
  • 28
1

Consider

def retry(attempts: Int)(func: => Unit) {

  for {
    i <- Stream range (0, attempts)
    v = Try (func()) toOption
    if (v == None)
  } ()

}

The for comprehension will invoke func up to an attempts number of times and will stop streaming if the invocation to func succeeds, namely if Try(func) toOption does not deliver None.

For each iteration in the for comprehension, the do-nothing () function is called.

If interested in details on each failure for func, consider replacing the Try in the comprehension with

v = Try(func()) match {
  case Success(x) => Some(())
  case Failure(x) => println(x) ; None
}

which preserves the semantics initially suggested, yet it extracts information on each failed attempt.

elm
  • 20,117
  • 14
  • 67
  • 113