Скала: возвращение имеет свое место
Рекомендации:
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, совпадет или сложится, что немного уродливо, если отступать для каждого вложенного условия.