0

I have a tail recursive implementation as below

@tailrec
def generate() : String = {
  val token = UUID.randomUUID().toString
  val isTokenExist = Await.result(get(token), 5.seconds).isDefined
  if(isTokenExist) generate()
  else token
}

get(token) will return a Future[Option[Token]].

I know that blocking in a Future is not good. I tried to return a Future[String] instead of String. But seems that its not possible unless I wait for isTokenExist to be completed.

Any other way/suggestion to implement this?

Max
  • 651
  • 8
  • 16
  • Why does it have to be tail-recursive ? Why does `generate` have to return String and not Future[String] ? If you answer these questions, you can probably have a better API design. Not trying to flip your question, but this seems like an anti-pattern for Future usage and unnecessary usage of tail -recursion, IMO. – Sudheer Aedama Aug 03 '15 at 22:39
  • Its actually what I'm looking for, different implementation which returns a `Future[String]`. But, I cant seem to find a solution. I would be glad if you can shed some light :) – Max Aug 04 '15 at 07:08

3 Answers3

0

What is happening in get(token) such that if it exceeds 5s the first time, it won't also exceed it the second time?

If you really need to check (and collisions are very unlikely), you probably just need to block anyway. Otherwise, if 5s is not enough, the method will never return and you will get a Stackoverflow after a very long time.

I would probably just keep it simple:

def generate() : String = {
  val token = UUID.randomUUID().toString
  if(get(token).isDefined) generate()
  else token
}

If you need to optimize something in this code, it probably is the get(token) method.

Jean Logeart
  • 52,687
  • 11
  • 83
  • 118
  • HI Jean, I'm using slick 3 to make a call to the db to `get(token)`. Seems that (please do correct me If I'm wrong) the return type must be a `Future`. Do you think it would be okay, to block on the `get(token)` ? – Max Aug 03 '15 at 17:31
0

if you wrap get(token) in a future, then you can use future onComplete function. Something like:

val f: Future[Token] = Future {
  get(token)
}
f onComplete {
     case Success(token) => generate()
     case Failure(t) => println("An error has occured: " + t.getMessage)
}

for more info take a look at: http://docs.scala-lang.org/overviews/core/futures.html

hi_my_name_is
  • 4,894
  • 3
  • 34
  • 50
0

generate does not have to be tail-recursive in this case since it uses Futures (read this: How do I make a function involving futures tail recursive? for explanation).

Will this work for you ?

  def generate: Future[String] = {
    val token = UUID.randomUUID().toString
    get(token).flatMap {
      case None => generate
      case _    => Future.successful(token)
    }
  }
Community
  • 1
  • 1
Sudheer Aedama
  • 2,116
  • 2
  • 21
  • 39
  • Yeap, thanks for the help. Just thought about it as well that it doesn't need to be tail recursive (Holy sh.. im dumb). Appreciate the help :) – Max Aug 04 '15 at 08:00
  • @Allan glad it works for you :) Also worth pointing at: http://stackoverflow.com/questions/24308286/future-recursion-patterns-future-chaining-of-arbitrary-length in case you want to retry-able future construct. – Sudheer Aedama Aug 04 '15 at 08:05