Явный возврат из действия воспроизведения

#scala #playframework

#scala #playframework

Вопрос:

У меня есть следующее действие

 def login: Result = Action(parse.json) { request =>
  if (/* Let's we say here is some validation */) {
    return BadRequest("bad")
  }

  Ok("all ok")
}
 

Это выдает мне Ошибку. Intellij предлагает Action[JsValue] тип. Когда я говорю, что возврат будет такого типа, я снова получаю сообщение об ошибке в BadRequest строке, потому что типы не совпадают.

Я попытался найти информацию об этой проблеме, и я нашел несколько ответов, которые предлагают установить Action[AnyContent] в качестве возвращаемого типа. Но я ВСЕ ЕЩЕ получаю ошибку.

Кроме того , мне нужно вернуться из if …Я не хочу писать else после этого if , потому что в какой-то более сложной функции, скорее всего, у меня будет несколько if операторов, которые должны прервать действие, и если я использую if/else подход, код станет кошмаром.

Комментарии:

1. Использование набора if инструкций для преждевременного возврата функции — не очень хорошая форма.

2. В любом случае я думаю, что это лучше, чем вложенность операторов if / else.

3. Какую версию фреймворка Play вы используете? Кроме того, вы получаете ошибку только в IntelliJ или вы также получаете ошибку при попытке компиляции с консоли? Если у вас есть несколько шагов проверки, вы думали о проверке тех, кто находится внутри a для понимания? Таким образом, вы можете сконденсировать свои возвраты BadResult / Ok в один фолд…

4. Если бы вы могли использовать if, в чем проблема использования else. Но вы могли бы попробовать подход сопоставления с образцом, используя case

5. В любом случае, это определенно оператор return, который вызывает вашу ошибку. Если вы собираетесь использовать операторы if для своей логики проверки, вам нужно работать в рамках ограничений, налагаемых ими (даже если это означает вложение if / else). Вся ваша проверка json должна обрабатываться чем-то вроде: val jsr = request.body.validate[T], а затем сворачиваться по jsr (что будет либо вашим предполагаемым результатом, либо Seq[(JsPath, Seq[ValidationError])] . Если вам также нужен другой тип проверки, используйте для понимания или другой фолд.

Ответ №1:

Вы не можете использовать return в этом контексте, потому что компилятор Scala неверно истолковывает то, что вы имеете в виду return . Вы намеревались return вернуть из функции, в которую передается Action.apply , то есть из этого блока:

 { request =>
  if (/* Let's we say here is some validation */) {
    return BadRequest("bad")
  }

  Ok("all ok")
}
 

Однако в Scala return нельзя использовать return функции from, только методы. Любой return интерпретируется как возврат из ближайшего окружающего метода. В этом случае компилятор думал, что вы возвращаетесь из login .

У вас включен неправильный тип login . login должно быть a Action[JsValue] . С этим правильным типом компилятор будет жаловаться на возврат, потому что вы возвращаете a SimpleResult вместо ожидаемого Action[JsValue] . При неправильном типе Result компилятор принимает return , но затем не доволен, потому что возвращаемое значение Action.apply всегда является типом Action .

Причина return , по которой это работает, заключается в поддержке циклов, таких как:

 def allPositive(l: List[Int]): Boolean = {
  l.foreach { x => if (x < 0) return false }
  true
}
 

Но использование return является однообразным. return В приведенном выше цикле реализовано исключение, поэтому оно неэффективно. И return может привести к очень запутанному коду:

 def login: Result = Action(parse.json) { request =>
  /*
  WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE
  WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE
  WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE
  WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE
  WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE
  WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE
  WALL OF CODE WALL OF CODE WALL OF CODE WALL with return  OF CODE
  WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE
  WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE
  WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE WALL OF CODE
  */

  Ok("all ok")
}
 

Очевидно, что это login всегда возвращается Ok . О, подождите … там return похоронен.

Ответ №2:

Конечно, это так. В Scala оператор «return» внутри вложенной анонимной функции реализуется путем выдвижения и перехвата NonLocalReturnException. Так сказано в спецификации языка Scala, раздел 6.20.

Это сделано из-за понимания, чтобы люди могли писать такой код:

 def loop() = {
  for (i <- 0 until 20) {
    if (someCondition) return
  }
}
 

Этот цикл на самом деле эквивалентен:

 (0 until 20).foreach { i => if (someCondition) return }
 

Вы видите анонимную функцию? Можете ли вы видеть, что рассматриваемый «возврат» не относится к возврату только из этой анонимной функции? На мой взгляд, это была дизайнерская ошибка. С другой стороны, на таком языке, как Scala, «возврат» в любом случае не нужен.

Итак, у вас есть анонимная функция:

 Action(parse.json) { request => ... this one here ... }
 

И внутри этой функции вы используете «return», который под капотом вызывает исключение.

Итак, общее эмпирическое правило в Scala — НИКОГДА, НИКОГДА НЕ ИСПОЛЬЗУЙТЕ RETURN . В любом случае, это язык, ориентированный на выражения. Вам это не нужно.

 Action(parse.json) { request =>
  if (/* Let's we say here is some validation */) 
    BadRequest("bad")
  else
    Ok("all ok")
}
 

Там гораздо больше идиоматики. КСТАТИ, у вас также нет «break» или «continue». Привыкайте работать без них. Кроме того, о вашем мнении:

Кроме того, МНЕ НУЖНО вернуться из if…Я не хочу писать else после этого if, потому что в какой-то более сложной функции, скорее всего, у меня будет несколько операторов if, которые должны прервать действие, и если я использую подход if / else, код будет кошмаром.

Это неправильно, и я ДЕЙСТВИТЕЛЬНО НЕНАВИЖУ работать с кодом, который полагается на return, break или continue для короткого замыкания логики ИМЕННО потому, что сложная логика запутывается, и я хочу иметь четкое представление о:

  1. инварианты, если мы говорим о цикле
  2. условия выхода
  3. ветви / пути

Использование return, break или continue разрушает ясность по всем 3 пунктам. Они не намного лучше, чем прыжки ГОТО. И если у вас есть сложные функции, разбейте их на несколько функций, если чтение этого начнет становиться проблемой.

Кроме того, гораздо лучше, чем несколько ветвей «if / else», являются операторы «match». Как только вы привыкнете к ним, они вам понравятся, особенно учитывая, что компилятор может даже защитить вас в некоторых случаях, когда вы пропускаете ветку.

Комментарии:

1. Спасибо за хорошее объяснение, но ИМХО эта часть scala отстой. Это правда, что мы можем использовать сопоставление с образцом и if /else вместо возврата к потоку функций управления, но у меня такое чувство, что вы можете легко закончить множеством вложенных операторов match / if / else только из-за дизайна scala. Scala обладает множеством дополнительных функций, и это разочаровывает, даже если вы не большой поклонник return .

2. @user232343 У меня было такое же чувство, когда я начал использовать Scala 3 года назад. Прямо сейчас я считаю это одной из его особенностей. Scala — это прежде всего функциональный / ориентированный на выражения язык, постарайтесь усвоить его и воздержитесь от суждений, пока не приобретете некоторый опыт. Я гарантирую вам, что вам это понравится 🙂