Нелокальное возвращение в Scala new?

Коллега просто показал мне это, и я был удивлен, что он скомпилирован вообще:

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 изнутри закрытия не разрешено. С каких это пор это сработало? Существуют ли оговорки с такими нелокальными возвращениями?

Ответ 1

Семантика относительно проста: return выкинет NonLocalReturnControl, который поймается при заключении метода toUpper. Это не похоже на недавнюю функцию; в Scala change-log не упоминается return с версии 2.0.

Здесь соответствующее описание из Scala Language Spec, раздел 6.20:

Возврат из вложенной анонимной функции осуществляется путем металирования и ловить scala.runtime.NonLocalReturnException. Любое исключение улавливание между точкой возврата и включенными методами см. исключение. Ключевое сравнение гарантирует, что эти исключения попадают только в экземпляр метода, который заканчивается возвратом.

Если выражение return само по себе является частью анонимной функции, оно возможно, что экземпляр окружения f уже вернулся перед выполнением выражения return. В этом случае брошенный scala.runtime.NonLocalReturnException не будет поймано и будет распространять стек вызовов.

Здесь пример, в котором выполняется NonLocalReturnControl:

var g: () => Unit = _
def f() { g = () => return }
f() // set g
g() // scala.runtime.NonLocalReturnControl$mcI$sp

Ответ 2

Допустим, что навсегда, более или менее. Там может показаться странным, но есть много мест, где обратное было бы правдой. Например:

// 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
}