Является ли нелокальное возвращение в 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
}
Другие вопросы по тегам