Отказ от ответственности: Прежде чем кто-то это скажет: да, я знаю, что это плохой стиль и не поощряется. Я делаю это только для того, чтобы поиграть со Scala и попытаться узнать больше о том, как работает система вывода типов и как настроить поток управления. Я не собираюсь использовать этот код на практике.
Итак: предположим, что я нахожусь в довольно длинной функции с множеством последовательных проверок в начале, которые, если они терпят неудачу, должны заставить функцию возвращать какое-то другое значение (не бросать), а в противном случае возвращать нормальное значение . Я не могу использовать return
в теле Function
. Но могу ли я смоделировать это? Немного похоже на то, что break
моделируется в scala.util.control.Breaks
?
Я придумал это:
object TestMain {
case class EarlyReturnThrowable[T](val thrower: EarlyReturn[T], val value: T) extends ControlThrowable
class EarlyReturn[T] {
def earlyReturn(value: T): Nothing = throw new EarlyReturnThrowable[T](this, value)
}
def withEarlyReturn[U](work: EarlyReturn[U] => U): U = {
val myThrower = new EarlyReturn[U]
try work(myThrower)
catch {
case EarlyReturnThrowable(`myThrower`, value) => value.asInstanceOf[U]
}
}
def main(args: Array[String]) {
val g = withEarlyReturn[Int] { block =>
if (!someCondition)
block.earlyReturn(4)
val foo = precomputeSomething
if (!someOtherCondition(foo))
block.earlyReturn(5)
val bar = normalize(foo)
if (!checkBar(bar))
block.earlyReturn(6)
val baz = bazify(bar)
if (!baz.isOK)
block.earlyReturn(7)
// now the actual, interesting part of the computation happens here
// and I would like to keep it non-nested as it is here
foo + bar + baz + 42 // just a dummy here, but in practice this is longer
}
println(g)
}
}
Мои проверки здесь, очевидно, фиктивные, но главное, что я хотел бы избежать чего-то подобного, когда действительно интересный код оказывается слишком вложенным, на мой вкус:
if (!someCondition) 4 else {
val foo = precomputeSomething
if (!someOtherCondition(foo)) 5 else {
val bar = normalize(foo)
if (!checkBar(bar)) 6 else {
val baz = bazify(bar)
if (!baz.isOK) 7 else {
// actual computation
foo + bar + baz + 42
}
}
}
}
Мое решение отлично работает здесь, и я могу вернуться раньше с 4 в качестве возвращаемого значения, если захочу. Проблема в том, что мне приходится явно указывать параметр типа [Int]
, что немного неудобно. Есть ли способ обойти это?
else if
), ваши фактические вычисления будут вложены только один раз по сравнению с теми, которые не вложены в ваш хак. В чем проблема с этим? В вашем примере все, что вы сохраняете, — это одно ключевое словоelse
, но у вас есть все накладные расходы. - person Raphael   schedule 08.06.2011earlyReturn
может быть вложенным, но не нуждается в фигурных скобках и может находиться на той же строке. Мой окончательный уровень вложенности вычислений не должен зависеть от количества проверенных ранее условий. - person Jean-Philippe Pellet   schedule 09.06.2011