Skip to content

Instantly share code, notes, and snippets.

@gakuzzzz
Forked from xuwei-k/not_tailrec.scala
Created July 4, 2012 17:31
Show Gist options
  • Select an option

  • Save gakuzzzz/3048489 to your computer and use it in GitHub Desktop.

Select an option

Save gakuzzzz/3048489 to your computer and use it in GitHub Desktop.

Revisions

  1. gakuzzzz revised this gist Jul 4, 2012. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions tailrec.scala
    Original file line number Diff line number Diff line change
    @@ -9,21 +9,21 @@ object RetryUtil {

    def retry[T](intervals: Stream[Int], shouldCatch: Throwable => Boolean)(f: => T): T = {
    @annotation.tailrec
    def _retry(ints: Stream[Int], errors: List[Throwable], f: => T): T = {
    def _retry(_intervals: Stream[Int], errors: List[Throwable]): T = {
    allCatch.either(f) match {
    case Right(r) => r
    case Left(e) =>
    if (shouldCatch(e)) {
    ints match {
    _intervals match {
    case head #:: tail =>
    Thread.sleep(head)
    _retry(tail, e :: errors, f)
    _retry(tail, e :: errors)
    case _ => throw RetryException(e :: errors)
    }
    } else throw e
    }
    }
    _retry(intervals, Nil, f)
    _retry(intervals, Nil)
    }

    }
  2. gakuzzzz revised this gist Jul 4, 2012. 4 changed files with 56 additions and 58 deletions.
    25 changes: 0 additions & 25 deletions not_tailrec.scala
    Original file line number Diff line number Diff line change
    @@ -1,25 +0,0 @@
    object RetryUtil {

    case class RetryException(throwables: List[Throwable]) extends Exception

    def retry[T](retryLimit: Int, retryInterval: Int, shouldCatch: Throwable => Boolean)(f: => T): T = {
    // @annotation.tailrec
    def _retry( errors: List[Throwable], f: => T):T = {
    try {
    f
    } catch {
    case e if shouldCatch(e) =>
    if (errors.size < retryLimit) {
    Thread.sleep(retryInterval)
    _retry( e :: errors, f)
    } else {
    throw RetryException(e :: errors)
    }
    }
    }
    _retry( Nil, f)
    }


    }

    7 changes: 2 additions & 5 deletions readme.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,2 @@
    *` List[Class[_]] ` に catch するべき Class が含まれているか? 」 ではなく、「 catch するべき例外かどうか? 」そのものを表す関数を渡すように変更した
    * 再帰のたびに毎回 List 内の error の数が1づつ増えてるはずなので、 `counter` のパラメータは List の size から取得するようにして消した
    * `retryLimit` 超えて例外が発生し続けた場合、結局その例外Listを最後に投げるので、関数内関数の内部で先になげてしまうようにした
    * 最初 `not_tailrec.scala` の方にしたけど、 [try catchは末尾再帰が効かないらしい](https://issues.scala-lang.org/browse/SI-1672) ので、最終的に [allCatch](https://github.com/scala/scala/blob/v2.9.2/src/library/scala/util/control/Exception.scala#L160) で一度 catch した後 match するようにした
    * [allCatch は本当に全部catchするわけではない](https://github.com/scala/scala/blob/v2.9.2/src/library/scala/util/control/Exception.scala#L39-45)ので、厳密にいうとちょっと動作変わってる
    * 1秒後, 5秒後, 15秒後, 60秒後 みたいなリトライがしたかったので interval に Stream[Int] を渡せるようにしてみた。
    * 無限 Stream 渡したら無限 retry もできるね。
    57 changes: 29 additions & 28 deletions tailrec.scala
    Original file line number Diff line number Diff line change
    @@ -1,28 +1,29 @@
    import scala.util.control.Exception.allCatch

    object RetryUtil {

    case class RetryException(throwables: List[Throwable]) extends Exception

    def retry[T](retryLimit: Int, retryInterval: Int, shouldCatch: Throwable => Boolean)(f: => T): T = {
    @annotation.tailrec
    def _retry( errors: List[Throwable], f: => T):T = {
    allCatch.either(f) match{
    case Right(r) => r
    case Left(e) =>
    if(shouldCatch(e)){
    if (errors.size < retryLimit) {
    Thread.sleep(retryInterval)
    _retry( e :: errors, f)
    } else {
    throw RetryException(e :: errors)
    }
    }else throw e
    }
    }
    _retry( Nil, f)
    }


    }

    import scala.util.control.Exception.allCatch

    object RetryUtil {

    case class RetryException(throwables: List[Throwable]) extends Exception

    def retry[T](retryLimit: Int, retryInterval: Int, shouldCatch: Throwable => Boolean)(f: => T): T =
    retry(Stream.fill(retryLimit)(retryInterval), shouldCatch)(f)

    def retry[T](intervals: Stream[Int], shouldCatch: Throwable => Boolean)(f: => T): T = {
    @annotation.tailrec
    def _retry(ints: Stream[Int], errors: List[Throwable], f: => T): T = {
    allCatch.either(f) match {
    case Right(r) => r
    case Left(e) =>
    if (shouldCatch(e)) {
    ints match {
    case head #:: tail =>
    Thread.sleep(head)
    _retry(tail, e :: errors, f)
    case _ => throw RetryException(e :: errors)
    }
    } else throw e
    }
    }
    _retry(intervals, Nil, f)
    }

    }
    25 changes: 25 additions & 0 deletions z_sample.scala
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,25 @@
    scala> RetryUtil.retry(1000 #:: 3000 #:: 10000 #:: Stream.empty[Int], {_ => true}) {
    | println(new java.util.Date)
    | throw new RuntimeException("hoge")
    | }
    Thu Jul 05 02:58:24 JST 2012
    Thu Jul 05 02:58:25 JST 2012
    Thu Jul 05 02:58:28 JST 2012
    Thu Jul 05 02:58:38 JST 2012
    RetryUtil$RetryException
    at RetryUtil$._retry$1(<console>:30)
    at RetryUtil$.retry(<console>:35)
    at .<init>(<console>:14)
    at .<clinit>(<console>)
    at .<init>(<console>:11)
    at .<clinit>(<console>)
    at $print(<console>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:704)
    at scala.tools.nsc.interpreter.IMain$Request$$anonfun$14.apply(IMain.scala:920)
    at scala.tools.nsc.interpreter.Line$$anonfun$1.apply$mcV$sp(Line.scala:43)
    at scala.tools.nsc.io.package$$anon$2.run(package.scala:25)
    at java.lang.Thread.run(Thread.java:722)
  3. @xuwei-k xuwei-k revised this gist Jun 26, 2012. 4 changed files with 58 additions and 30 deletions.
    30 changes: 0 additions & 30 deletions gistfile1.scala
    Original file line number Diff line number Diff line change
    @@ -1,30 +0,0 @@
    object RetryUtil {

    case class RetryException(throwables: List[Throwable]) extends Exception


    def retry[T](retryLimit: Int, retryInterval: Int, exceptionClasses: List[Class[_]])(f: => T): T = {
    def _retry[T](counter: Int = 1, eithers: List[Either[Throwable, T]] = List.empty, f: => T):List[Either[Throwable,T]] = {
    try {
    val r = f
    eithers :+ Right(r)
    } catch {
    case e: Throwable =>
    (exceptionClasses.find(_ == e.getClass).isDefined, counter < retryLimit) match {
    case (true, true) =>
    Thread.sleep(retryInterval)
    _retry(counter + 1, eithers :+ Left(e), f)
    case (true, false) =>
    eithers
    case _=>
    throw e
    }
    }
    }
    _retry(f).find(_.isRight).getOrElse {
    throw RetryException(eithers.map(_.left.get).toList)
    }.right.get
    }


    }
    25 changes: 25 additions & 0 deletions not_tailrec.scala
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,25 @@
    object RetryUtil {

    case class RetryException(throwables: List[Throwable]) extends Exception

    def retry[T](retryLimit: Int, retryInterval: Int, shouldCatch: Throwable => Boolean)(f: => T): T = {
    // @annotation.tailrec
    def _retry( errors: List[Throwable], f: => T):T = {
    try {
    f
    } catch {
    case e if shouldCatch(e) =>
    if (errors.size < retryLimit) {
    Thread.sleep(retryInterval)
    _retry( e :: errors, f)
    } else {
    throw RetryException(e :: errors)
    }
    }
    }
    _retry( Nil, f)
    }


    }

    5 changes: 5 additions & 0 deletions readme.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,5 @@
    *` List[Class[_]] ` に catch するべき Class が含まれているか? 」 ではなく、「 catch するべき例外かどうか? 」そのものを表す関数を渡すように変更した
    * 再帰のたびに毎回 List 内の error の数が1づつ増えてるはずなので、 `counter` のパラメータは List の size から取得するようにして消した
    * `retryLimit` 超えて例外が発生し続けた場合、結局その例外Listを最後に投げるので、関数内関数の内部で先になげてしまうようにした
    * 最初 `not_tailrec.scala` の方にしたけど、 [try catchは末尾再帰が効かないらしい](https://issues.scala-lang.org/browse/SI-1672) ので、最終的に [allCatch](https://github.com/scala/scala/blob/v2.9.2/src/library/scala/util/control/Exception.scala#L160) で一度 catch した後 match するようにした
    * [allCatch は本当に全部catchするわけではない](https://github.com/scala/scala/blob/v2.9.2/src/library/scala/util/control/Exception.scala#L39-45)ので、厳密にいうとちょっと動作変わってる
    28 changes: 28 additions & 0 deletions tailrec.scala
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,28 @@
    import scala.util.control.Exception.allCatch

    object RetryUtil {

    case class RetryException(throwables: List[Throwable]) extends Exception

    def retry[T](retryLimit: Int, retryInterval: Int, shouldCatch: Throwable => Boolean)(f: => T): T = {
    @annotation.tailrec
    def _retry( errors: List[Throwable], f: => T):T = {
    allCatch.either(f) match{
    case Right(r) => r
    case Left(e) =>
    if(shouldCatch(e)){
    if (errors.size < retryLimit) {
    Thread.sleep(retryInterval)
    _retry( e :: errors, f)
    } else {
    throw RetryException(e :: errors)
    }
    }else throw e
    }
    }
    _retry( Nil, f)
    }


    }

  4. @j5ik2o j5ik2o revised this gist Jun 26, 2012. 1 changed file with 8 additions and 8 deletions.
    16 changes: 8 additions & 8 deletions gistfile1.scala
    Original file line number Diff line number Diff line change
    @@ -4,20 +4,20 @@ object RetryUtil {


    def retry[T](retryLimit: Int, retryInterval: Int, exceptionClasses: List[Class[_]])(f: => T): T = {
    def _retry[T](counter: Int, eithers: List[Either[Throwable, T]], f: => T):List[Either[Throwable,T]] = {
    def _retry[T](counter: Int = 1, eithers: List[Either[Throwable, T]] = List.empty, f: => T):List[Either[Throwable,T]] = {
    try {
    val r = f
    eithers :+ Right(r)
    } catch {
    case e: Throwable =>
    (exceptionClasses.find(_ == e.getClass).isDefined, counter < retryLimit) match {
    case (true, true) =>
    Thread.sleep(retryInterval)
    _retry(counter + 1, eithers :+ Left(e), f)
    case (true, false) =>
    eithers
    case _=>
    throw e
    case (true, true) =>
    Thread.sleep(retryInterval)
    _retry(counter + 1, eithers :+ Left(e), f)
    case (true, false) =>
    eithers
    case _=>
    throw e
    }
    }
    }
  5. @j5ik2o j5ik2o revised this gist Jun 26, 2012. 1 changed file with 7 additions and 9 deletions.
    16 changes: 7 additions & 9 deletions gistfile1.scala
    Original file line number Diff line number Diff line change
    @@ -7,23 +7,21 @@ object RetryUtil {
    def _retry[T](counter: Int, eithers: List[Either[Throwable, T]], f: => T):List[Either[Throwable,T]] = {
    try {
    val r = f
    List(Right(r))
    eithers :+ Right(r)
    } catch {
    case e: Throwable =>
    if (exceptionClasses.find(_ == e.getClass).isDePfined) {
    if (counter < retryLimit) {
    (exceptionClasses.find(_ == e.getClass).isDefined, counter < retryLimit) match {
    case (true, true) =>
    Thread.sleep(retryInterval)
    _retry(counter + 1, Left(e) :: eithers, f)
    } else {
    _retry(counter + 1, eithers :+ Left(e), f)
    case (true, false) =>
    eithers
    }
    } else {
    case _=>
    throw e
    }
    }
    }
    val eithers = _retry(1, List.empty, f)
    eithers.find(_.isRight).getOrElse {
    _retry(f).find(_.isRight).getOrElse {
    throw RetryException(eithers.map(_.left.get).toList)
    }.right.get
    }
  6. @j5ik2o j5ik2o revised this gist Jun 26, 2012. 1 changed file with 9 additions and 10 deletions.
    19 changes: 9 additions & 10 deletions gistfile1.scala
    Original file line number Diff line number Diff line change
    @@ -10,20 +10,19 @@ object RetryUtil {
    List(Right(r))
    } catch {
    case e: Throwable =>
    val _eithers = if (exceptionClasses.find(_ == e.getClass).isDefined) {
    Thread.sleep(retryInterval)
    Left(e) :: eithers
    if (exceptionClasses.find(_ == e.getClass).isDePfined) {
    if (counter < retryLimit) {
    Thread.sleep(retryInterval)
    _retry(counter + 1, Left(e) :: eithers, f)
    } else {
    eithers
    }
    } else {
    throw e
    }
    if (counter < retryLimit) {
    _retry(counter + 1, _eithers, f)
    } else {
    eithers
    }
    }
    }
    }
    val eithers = _retry(0, List.empty, f)
    val eithers = _retry(1, List.empty, f)
    eithers.find(_.isRight).getOrElse {
    throw RetryException(eithers.map(_.left.get).toList)
    }.right.get
  7. @j5ik2o j5ik2o renamed this gist Jun 26, 2012. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  8. @j5ik2o j5ik2o created this gist Jun 26, 2012.
    33 changes: 33 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,33 @@
    object RetryUtil {

    case class RetryException(throwables: List[Throwable]) extends Exception


    def retry[T](retryLimit: Int, retryInterval: Int, exceptionClasses: List[Class[_]])(f: => T): T = {
    def _retry[T](counter: Int, eithers: List[Either[Throwable, T]], f: => T):List[Either[Throwable,T]] = {
    try {
    val r = f
    List(Right(r))
    } catch {
    case e: Throwable =>
    val _eithers = if (exceptionClasses.find(_ == e.getClass).isDefined) {
    Thread.sleep(retryInterval)
    Left(e) :: eithers
    } else {
    throw e
    }
    if (counter < retryLimit) {
    _retry(counter + 1, _eithers, f)
    } else {
    eithers
    }
    }
    }
    val eithers = _retry(0, List.empty, f)
    eithers.find(_.isRight).getOrElse {
    throw RetryException(eithers.map(_.left.get).toList)
    }.right.get
    }


    }