Скала: возвращение имеет свое место

Рекомендации:
Scala возвращаемое ключевое слово
обработка ошибок в контроллерах scala

EDIT3
Это «окончательный» Решение, снова благодаря Дэн Бертон.

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)
}

Затем метод проекта pimp 'a Either, размещенный где-то в вашем приложении (в моем случае это признак, который указывает на то, что sbt root & amp; дочерний проект (ы) расширяет свой объект базового пакета из:

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
Эволюция, от встроенных операторов возврата до этого маленького белого карлика плотности (слава @DanBurton, негодяй Хаскелла ;-))

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)
  ...
}

Я добавил проекцию Dan onLeft Either в качестве сутенера к Either с вышеупомянутым "project" quot; метод, который учитываетeitherResult project(left-outcome), По сути, вы получаете ошибку «первый сбой» как «Левый», а успех как «Правый», что не сработает при подаче результатов Варианта для понимания (вы получаете только результат «Некоторые / Нет»).

Единственное, что меня не радует, - это указание типа дляproject(Conflict(param)); Я думал, что компилятор сможет вывести левый тип условия из того, что передается ему: очевидно, нет.

Во всяком случае, ясно, что функциональный подход устраняет необходимость во встроенных операторах возврата, как я пытался сделать с императивным подходом if / else.

EDIT
Функциональный эквивалент:

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
        )}
    )}
)

это, конечно, плотно упакованный блок кода, там много чего происходит; тем не менее, я бы сказал, что соответствующий императивный код со встроенными возвратами не только одинаково лаконичен, но и проще в использовании (с дополнительным преимуществом меньшего количества конечных фигур и паренсов, которые нужно отслеживать)

ORIGINAL
Нахождение себя в императивной ситуации; хотел бы увидеть альтернативный подход к следующему (который не работает из-за использования ключевого слова return и отсутствия явного типа в методе):

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
    }
  }
}

В этом случае мне нравится использование return, так как вы избегаете вложения нескольких блоков if / else, или сгибов, или совпадений, или не обязательного заполнения. Проблема, конечно, в том, что он не работает, должен быть указан явный тип возврата, который имеет свои проблемы, так как мне еще предстоит выяснить, как указать тип, который удовлетворяет любой магии Play на работе - нет,def save: Result, не работает компилятор потом жалуетсяimplicit result теперь не имеет явного типа ;-(

В любом случае, примеры игровых фреймворков предоставляют условие «la, la, la, la, la happy 1-shot-сделки» (ошибка, успех), что не всегда имеет место в реальном мире & # x2122; ;-)

Так что же такое идиоматический эквивалент (без использования возврата) вышеуказанному блоку кода? Я предполагаю, что он будет вложенным, если / else, совпадет или сложится, что немного уродливо, если отступать для каждого вложенного условия.

Ответы на вопрос(4)

Ваш ответ на вопрос