Scala: return has its place

前端 未结 4 1775
北荒
北荒 2020-12-14 11:14

References:
Scala return keyword
handling errors in scala controllers

EDIT3
This is the \"final\" solution, again thanks to Dan Burton.<

4条回答
  •  隐瞒了意图╮
    2020-12-14 11:29

    Scala internally uses the throw/catch mechanism to handle returns in places where returns are syntactically okay but it actually has to jump out of several methods. So you can either let it do this:

    def save = Action { implicit request =>
      def result(): Foo = {
        /* All your logic goes in here, including returns */
      }
      result()
    }
    

    or, if you prefer, you can use your own data-passing throwable class (without stack trace):

    import scala.util.control.ControlThrowable
    case class Return[A](val value: A) extends ControlThrowable {}
    
    def save = Action { implicit request =>
      try {
        /* Logic */
        if (exitEarly) throw Return(Ok(blahBlah))
        /* More logic */
      }
      catch {
        case Return(x: Foo) => x
      }
    }
    

    Or you could get a little fancier and add your own exception handling:

    case class Return[A](val value: A) extends ControlThrowable {}
    class ReturnFactory[A]{ def apply(a: A) = throw new Return(a) }
    def returning[A: ClassManifest](f: ReturnFactory[A] => A) = {
      try { f(new ReturnFactory[A]) } catch {
        case r: Return[_] =>
          if (implicitly[ClassManifest[A]].erasure.isAssignableFrom(r.value.getClass)) {
            r.value.asInstanceOf[A]
          } else {
            throw new IllegalArgumentException("Wrong Return type")
          }
      } 
    }
    

    (If you want to be able to nest the returnings, just rethrow the Return instead of throwing an IllegalArgumentException when the type doesn't match.) You can use this like so:

    def bar(i: Int) = returning[String] { ret =>
      if (i<0) ret("fish")
      val j = i*4
      if (j>=20) ret("dish")
      "wish"*j
    }
    
    bar(-3)   // "fish"
    bar(2)    // "wishwishwishwishwishwishwishwish"
    bar(5)    // "dish"
    

    or in your particular case

    def save = Action{ implicit request => returning[Foo] { ret =>
      /* Logic goes here, using ret(foo) as needed */
    }}
    

    It's not built in, but it shouldn't be terribly hard to explain to people how to use this even if it's not so easy to understand how the capability is built. (Note: Scala does have built in break capability in scala.util.control.Breaks which uses something very much like this strategy.)

提交回复
热议问题