Scala: el retorno tiene su lugar.

Referencias:
Palabra clave de retorno de Scala
Manejo de errores en controladores scala.

EDITAR3
Esta es la solución "final", de nuevo gracias a Dan Burton.

def save = Action { implicit request =>
  val(orderNum, ip) = (generateOrderNum, request.remoteAddress)
  val result = for {
    model   <- bindForm(form).right // error condition already json'd
    transID <- payment.process(model, orderNum) project json
    userID  <- dao.create(model, ip, orderNum, transID) project json
  } yield (userID, transID)
}

Luego, el método pimp'd Either project, colocado en algún lugar de su aplicación (en mi caso, un rasgo implícito en el que los proyectos sbt root y child extienden su objeto de paquete base desde:

class EitherProvidesProjection[L1, R](e: Either[L1, R]) {
  def project[L1, L2](f: L1 => L2) = e match {
    case Left(l:L1) => Left(f(l)).right
    case Right(r)   => Right(r).right
  }
}
@inline implicit final def either2Projection[L,R](e: Either[L,R]) = new EitherProvidesProjection(e)

EDIT2
Evolución, han pasado de declaraciones de devolución incrustadas a esta pequeña enana blanca de densidad (felicitaciones a @DanBurton, el pícaro de Haskell ;-))

def save = Action { implicit request =>
  val(orderNum, ip) = (generateOrderNum, request.remoteAddress)
  val result = for {
    model   <- form.bindFromRequest fold(Left(_), Right(_)) project( (f:Form) => Conflict(f.errorsAsJson) )
    transID <- payment.process(model, orderNum) project(Conflict(_:String))
    userID  <- dao.create(model, ip, orderNum, transID) project(Conflict(_:String))
  } yield (userID, transID)
  ...
}

He añadido la proyección OnLeft Oither de Dan como un proxeneta a cualquiera de los dos, con el método anterior del "proyecto", que permite un sesgo correctoeitherResult project(left-outcome). Básicamente, usted obtiene el error de fallar primero como una izquierda y el éxito como una derecha, algo que no funcionaría cuando alimenta los resultados de la Opción para su comprensión (solo obtiene el resultado de Algunos / Ninguno).

Lo único que no me emociona es tener que especificar el tipo para elproject(Conflict(param)); Pensé que el compilador sería capaz de inferir el tipo de condición izquierda del O bien que se le está pasando: aparentemente no.

En cualquier caso, está claro que el enfoque funcional evita la necesidad de declaraciones de devolución integradas, como estaba tratando de hacer con el enfoque imperativo.

EDITAR
El equivalente funcional es:

val bound = form.bindFromRequest
bound fold(
  error=> withForm(error),
  model=> {
    val orderNum = generateOrderNum()
    payment.process(model, orderNum) fold (
      whyfail=> withForm( bound.withGlobalError(whyfail) ),
      transID=> {
        val ip = request.headers.get("X-Forwarded-For")
        dao.createMember(model, ip, orderNum, transID) fold (
          errcode=> 
            Ok(withForm( bound.withGlobalError(i18n(errcode)) )),
          userID=> 
            // generate pdf, email, redirect with flash success
        )}
    )}
)

que es ciertamente un bloque de código densamente cargado de energía, muchas cosas suceden allí; sin embargo, yo diría que el código imperativo correspondiente con devoluciones incrustadas no solo es conciso de manera similar, sino que también es más fácil de asimilar (con el beneficio agregado de menos curlies y parens para seguir)

ORIGINAL
Me encuentro en una situación imperativa; Me gustaría ver un enfoque alternativo a lo siguiente (que no funciona debido al uso de la palabra clave return y la falta de un tipo explícito en el método):

def save = Action { implicit request =>
  val bound = form.bindFromRequest
  if(bound.hasErrors) return Ok(withForm(bound))

  val model = bound.get
  val orderNum = generateOrderNum()
  val transID  = processPayment(model, orderNum)
  if(transID.isEmpty) return Ok(withForm( bound.withGlobalError(...) ))

  val ip = request.headers.get("X-Forwarded-For")
  val result = dao.createMember(model, ip, orderNum, transID)
  result match {
    case Left(_) => 
      Ok(withForm( bound.withGlobalError(...) ))
    case Right((foo, bar, baz)) =>
      // all good: generate pdf, email, redirect with success msg
    }
  }
}

En este caso, me gusta el uso de retorno, ya que evita anidar varios bloques if / else, o pliegues, o partidos, o el enfoque de imperativo de relleno en el espacio en blanco. El problema, por supuesto, es que no funciona, se debe especificar un tipo de retorno explícito, que tiene sus propios problemas, ya que todavía tengo que averiguar cómo especificar un tipo que satisfaga cualquier juego de magia de juego en acción, no,def save: Result, no funciona como el compilador luego se queja deimplicit result ahora no tiene un tipo explícito ;-(

En cualquier caso, los ejemplos del marco de juego proporcionan la condición (, error, éxito) de fold, deal, lafat, 1-shot-happy, que no siempre es el caso en el mundo real ™ ;-)

Entonces, ¿cuál es el equivalente idiomático (sin uso de retorno) al bloque de código anterior? Supongo que sería anidado si / else, match o fold, que se pone un poco feo, sangrando con cada condición anidada.

Respuestas a la pregunta(4)

Su respuesta a la pregunta