Подтвердить что ты не робот

Scala операторы возврата в анонимных функциях

Почему явный оператор return (тот, который использует ключевое слово return) в анонимной функции возвращает из включенной именованной функции, а не только из анонимной функции?

например. следующая программа приводит к ошибке типа:

def foo: String = {
  ((x: Integer) => return x)
  "foo"
}

Я знаю, что рекомендуется избегать ключевого слова return, но меня интересует, почему явные и неявные операторы return имеют другую семантику в анонимных функциях.

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

def main(args: Array[String]) {
  m(3)
}

def m: (Integer => Unit) =
  (x: Integer) => return (y: Integer) => 2
4b9b3361

Ответ 1

Формально говорящий возврат определяется как всегда возвращающийся из ближайшего охватывающего именованного метода

Возвращаемое выражение return e должно происходить внутри тела некоторого включая именованный метод или функцию. Самая внутренняя оболочка, названная метод или функция в исходной программе, f, должен иметь явно объявленный тип результата, и тип e должен соответствовать ему. Возврат выражение оценивает выражение e и возвращает его значение как результат f. Оценка любых выражений или выражений после возвращаемого выражения опускается.

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

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

Теперь, что касается "почему". Еще одна причина - эстетика: lambdas - это выражения, и это хорошо, когда выражение и все его подвыражения имеют одинаковое значение независимо от структуры гнездования. Нил Гафтер рассказывает об этом http://gafter.blogspot.com/2006/08/tennents-correspondence-principle-and.html

Основная причина, по которой она существует, заключается в том, что она позволяет легко моделировать формы потока управления, обычно используемые в императивном программировании, но все же позволяет абстрагироваться от функций более высокого порядка. В качестве примера игрушки конструкция Java foreach ( "для x: xs {yada;}" ) позволяет возвращать внутри цикла. Scala не имеет языкового уровня foreach. Вместо этого он помещает foreach в библиотеку (не считая "для выражения" без урожая, поскольку они просто desugar для foreach). Наличие нелокального возврата означает, что вы можете использовать Java foreach и переводить непосредственно в Scala foreach.

Кстати, Ruby, Smalltalk и Common Lisp (с моей головы) также имеют похожие "нелокальные" возвращения.

Ответ 2

Ключевое слово return зарезервировано для (класс) методов, оно не может использоваться в функциях. Вы можете легко проверить это:

object Foo {
  val bar = (i: Int) => return i + i
}

Это дает

<console>:42: error: return outside method definition
       object Foo { val bar = (i: Int) => return i + i }
                                          ^

В основном вы можете рассматривать методы и функции как одно и то же, поскольку метод apply работает синтаксически, как вызов метода, и так называемое eta-расширение, позволяющее передать метод как аргумент функции.

В этом случае это имеет значение. При определении как метода это законно:

object Foo { 
  def bar(i: Int): Int = return i + i
}

Таким образом, вы должны использовать только return в методах, которые позволяют условные (ранние) возвращения. См. этот пост для обсуждения методов и функций.