I'm making requests to the remote server and sometimes requests fail because of the unreliable network. In case of fail I want request to repeat, but n
times at max. If I were using imperative language, I would place request-sending code in a while-loop, but I want to do it in a functional way.
I've wrote helper for this purpose:
/** Repeatedly executes function `f`
* while predicate `p` holds
* but no more than `nTries` times.
*/
def repeatWhile[A](f: => A)(p: A => Boolean)(nTries: Int): Option[A] =
if (nTries == 0) {
None
} else {
f match {
case a if p(a) => repeatWhile(f)(p)(nTries - 1)
case a => Some(a)
}
}
And using it like this:
// Emulating unreliable connection
var n = 0
def receive(): Option[String] =
if (n < 4) {
n += 1
println("No result...")
None
} else {
println("Result!")
Some("Result")
}
// Repeated call
val result = repeatWhile(receive)(!_.isDefined)(10)
where receive
is a silly function for testing purposes. This code makes 4 calls before receive
finally succeeds with Some(Result)
:
No result...
No result...
No result...
No result...
Result!
My repeatWhile
works fine, but I feel like reinventing the wheel. I'm studying functional programming and want to know if there are simple/standard solutions to my problem.
P.s. I've defined even more helpers, maybe they are already in language/standard library?
/** Repeatedly executes function `f`
* while predicated `p` not holds
* but no more than `nTries` times.
*/
def repeatWhileNot[A](f: => A)(p: A => Boolean)(nTries:Int): Option[A] =
repeatWhile(f)(!p(_))(nTries)
/** Repeatedly executes function `f`
* while it returns None
* but no more than `nTries` times.
*/
def repeatWhileNone[A](f: => Option[A])(nTries:Int): Option[A] =
repeatWhileNot(f)(_.isDefined)(nTries).getOrElse(None)
The canonical way would be to use an Iterator
:
Iterator.continually{f}.take(nTries).dropWhile(!p).take(1).toList
which will give you either an empty list or a one-item list, depending on whether it was successful. You can convert this to an option with headOption
if you so desire. With minor modifications this works on all your use cases.
Writing small recursive methods as you've done is perfectly sensible also, though they aren't in the library. In general, writing helper methods for what you do most is a very good idea. This is one reason why Scala makes it so easy to write methods.
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