Литература:
Scala ключевое слово return
обработка ошибок в контроллерах 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'd Любой метод проекта, помещенный где-то в ваше приложение (в моем случае, свойство implicits, что sbt root и дочерний проект расширяет свой базовый объект пакета из:
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
Evolution, перешли от встроенных возвратных операторов к этому маленькому белому карлику плотности (kudos to @DanBurton, Haskell rascal; -))
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. Или проецирование в качестве сутенера, либо с помощью вышеописанного метода "project", который допускает правое смещение eitherResult project(left-outcome)
. В основном вы получаете ошибку с ошибкой как левый и успех как правую, то, что не сработает при подаче результатов Варианта для понимания (вы получаете только результат Some/None).
Единственное, с чем я не в восторге, - это указать тип для project(Conflict(param))
; Я думал, что компилятор сможет вывести тип левого состояния из Лица, который передается ему: видимо, нет.
Во всяком случае, ясно, что функциональный подход устраняет необходимость в встроенных операторах return, как я пытался сделать с if/else императивным подходом.
ИЗМЕНИТЬ
Функциональный эквивалент:
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
)}
)}
)
который, безусловно, представляет собой плотно упакованный блок кода, много чего там происходит; Тем не менее, я бы сказал, что соответствующий императивный код со встроенными результатами не только аналогичен кратким, но и более легким для поиска (с дополнительным преимуществом меньшего количества конических завитушек и парсеров для отслеживания)
ОРИГИНАЛ
Нахождение себя в императивной ситуации; хотел бы увидеть альтернативный подход к следующему (который не работает из-за использования ключевого слова 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, или сгибов, или совпадений, или незаполненного неинвазивного подхода. Проблема, конечно, в том, что она не работает, должен указываться явный тип возвращаемого значения, который имеет свои собственные проблемы, поскольку мне еще предстоит выяснить, как указать тип, который удовлетворяет любой магии игры, - нет, def save: Result
, не работает, поскольку компилятор тогда жалуется на implicit result
, теперь не имеющий явного типа; - (
Во всяком случае, примеры Framework для игры обеспечивают la, la, la, la happy 1-shot-deal fold (ошибка, успех), которое не всегда имеет место в реальном мире и торговле;; -)
Итак, каков идиоматический эквивалент (без использования возврата) на предыдущий блок кода? Я предполагаю, что он будет вложен, если /else, match или fold, который становится немного уродливым, отступы с каждым вложенным условием.