Является ли нелокальное возвращение в Scala новым?
Коллега только что показал мне это, и я был удивлен, что это вообще скомпилировано:
def toUpper(s: Option[String]): String = {
s.getOrElse(return "default").toUpperCase
// ^^^^^^ // a return here in the closure??
}
и это даже работает:
println(toUpper(Some("text"))) // TEXT
println(toUpper(None)) // default
я думал return
изнутри закрытие не допускалось. С каких пор это сработало? Есть ли предостережения с такими нелокальными доходами?
2 ответа
Семантика относительно проста: return
бросит NonLocalReturnControl
который пойман при включении метода, toUpper
, Это не похоже на недавнюю особенность; нет упоминания о return
в журнале изменений Scala начиная с версии 2.0.
Вот соответствующее описание из спецификации языка Scala, раздел 6.20:
Возврат из вложенной анонимной функции осуществляется путем генерирования и перехвата исключения scala.runtime.NonLocalReturnException. Любое исключение, пойманное между точкой возврата и включающими методами, может увидеть исключение. Сравнение ключей гарантирует, что эти исключения перехватываются только экземпляром метода, который завершается возвратом.
Если возвращаемое выражение само является частью анонимной функции, возможно, что включающий экземпляр f уже возвращен до выполнения возвращаемого выражения. В этом случае выброшенное исключение scala.runtime.NonLocalReturnException не будет перехвачено и будет распространяться вверх по стеку вызовов.
Вот пример, в котором NonLocalReturnControl
ускользает:
var g: () => Unit = _
def f() { g = () => return }
f() // set g
g() // scala.runtime.NonLocalReturnControl$mcI$sp
Это было разрешено с тех пор навсегда, более или менее. Там это может выглядеть странно, но есть много мест, где обратное будет правдой. Например:
// excessive use of braces for the sake of making scopes clearer
def findFirst[A](l: List[A])(p: A => Boolean): Option[A] = {
for (x <- l) {
if (p(x)) return Some(x)
}
None
}