2016-10-21 23 views
3

私はスカラに再帰的な再試行関数を書いています。将来の作成時に実行時エラーがあるかどうかを知りたいと思っています。その場合、将来のインスタンス化が再試行されます。スカラ再帰関数の戻り値の型が

は、私は、データベースクエリ機能を持っている想像:

dbLookup(userId : UserId) : Option[UserName] = ??? 

再試行は、フォームのようなものになります:

retry[T](f :() => Future[Option[T]], 
     notifyFailure : (t : Throwable) => Unit, 
     n : Int) : Future[Option[T]] = { 
    if(n <= 0) { Future{None} } 
    else { 
    val fut = f() 
    if(f.resultsInException) {  //I don't know how to write this 
     notifyFailure(f.exception) 
     retry(f, notifyFailure, n-1) //try again 
    } 
    else { 
     f.originalValueAsFuture 
    } 
    } 
} 

はどのようにこの将来の機能が実装され、末尾再帰の最適化を可能にすることができますか?

この関数は、実行コンテキストは、私が将来を作成しようとすると例外をスローし続ける場合は、ユーザーのためのデータベースを10回再試行するために使用することができ

val userNameAfterRetries = 
    retry(() => Future{dbLookup("1234")}, 
     (t) => system error (s"future creation error : $t"), 
     10) 

注:これは可能性の一種でありますFuture.fallbackToですが、残念ながらfallbackToは() => Future[T]ではなくFuture[T]になります。これは、最初の試行が成功した場合でも、fallbackToを使用すると余分な時間が少なくとも1回は再試行されるため、重要です。

お気軽にご相談ください。

+0

Futureから「onFailure」を使用できますか?多分私は何かを見逃しているでしょうか? – Jegan

+0

@Jegan 'onFailure'は、失敗した場合に実行するnotifyFailureを「登録」できますが、再試行の値を返す必要があります... –

答えて

4

これはいかがですか?末尾再帰上

def retry[T](f:() => Future[Option[T]], 
       notifyFailure: Throwable => Unit, 
       n: Int)(implicit ec : ExecutionContext): Future[Option[T]] = { 
    if (n <= 0) Future.failed(new RuntimeException("Exceeded number of allowed retries")) 
    else f().recoverWith { case originalError => notifyFailure(originalError); retry(f, notifyFailure, n - 1) } 
    } 

更新:Futureの性質はとても非同期であるあなたが結果をawaitする場合を除き、あなたはコールバックで再帰を使用する必要がありますので、私はかなりそれ@tailrecにすることが可能であることが表示されません。

また、実用的なノート:あなたがそれが常に10回の再試行であることを知っているなら、私は再帰を恐れないでしょう。

+0

更新:実際には可能な限り最も単純な解決策です:) – tkachuko

+0

私は、問題は、尾の再帰的な解を求める。 – Jegan

+0

実際には、 "else"部分を見て、 "再試行"メソッドの再帰呼び出しで将来を回復します – tkachuko