Почему в Kotlin есть два синтаксиса для лямбда-выражений / анонимных функций?

В Kotlin есть два способа объявления анонимной функции (также известной как лямбда). Два разных синтаксиса:

val lambda =  { input : String -> 
  "received ${input}"
}

а также

val anonymousFunction =  fun (input : String): String {
  return "received ${input}"
}

Я понимаю разницу между ними (как указано в этом ответе), но что я Не понимаю, почему в языке есть два разных способа объявить одно и то же.

Есть ли оптимизация под капотом одного по сравнению с другим? Версия анонимной функции была сочтена слишком многословной? Может ли лямбда-версия синтаксиса не поддерживать возвращаемый тип?


person gypsydave5    schedule 05.01.2018    source источник
comment
Связанные вопросы показывают некоторые преимущества анонимных функций по сравнению с лямбдами.   -  person s1m0nw1    schedule 05.01.2018


Ответы (1)


Основная причина - поддержка возврата из лямбда-выражений. Правило для возвратов состоит в том, что ключевое слово return возвращается из ближайшей функции, объявленной с ключевым словом fun. В некоторых сценариях вы хотите, чтобы return в блоке кода возвращал из включающей функции, поэтому вы используете лямбда:

fun processElements(list: List<Element>): Boolean {
     list.forEach { element ->
          if (!element.process()) return false   // returns from processElements()
     }
     return true
}

В других сценариях вы хотите вернуться из блока, но не из включающей функции. При использовании лямбда для возврата из блока требуется синтаксис return@label, который несколько неуклюжий:

fun processElements(list: List<Element>) {
     list.forEach { element ->
          if (!needToProcessElement(element)) return@forEach // returns from block
          element.process()
     }
}

Чтобы избежать неуклюжести, мы предоставили альтернативный синтаксис - анонимные функции - который позволяет вам использовать return напрямую для возврата из блока:

fun processElements(list: List<Element>) {
    list.forEach(fun(element) { 
        if (!needToProcessElement(element)) return // returns from block
        element.process()
    })
}

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

person yole    schedule 05.01.2018
comment
Итак, если я понимаю вышеизложенное, синтаксис помеченного возврата для лямбда-выражения считался неэлегантным, и поэтому был введен синтаксис анонимной функции, позволяющий получать немаркированные ранние возвраты в блоках. - person gypsydave5; 05.01.2018
comment
Это не так, как это происходило исторически в ходе эволюции версий Kotlin до 1.0, но именно поэтому у нас есть два синтаксиса в 1.0. - person yole; 05.01.2018
comment
Я предполагаю, что более элегантным решением могло бы быть использование только лямбда-выражений и всегда иметь немаркированный возвратный переход к прямому вызывающему лямбда-выражению. Поэтому, если вы хотите вернуться дальше, вам нужно будет указать это явно (return @ processElements), так как это оператор с большим количеством побочных эффектов с точки зрения labmda (обычно он должен принимать только аргументы и вернуть результат), и я считаю, что это должна быть ситуация, которая должна быть явно обозначена. Наличие двух семантик для немаркированного оператора return может быть источником простых ошибок. - person Chris Lercher; 24.10.2019
comment
@ChrisLercher Причина, по которой в Kotlin есть такой синтаксис, заключается в том, что мы хотели, чтобы лямбда-выражения были прозрачной заменой для конструкций потока управления (например, в Kotlin нет оператора synchronized, как в Java, потому что мы хотели реализовать его как функцию), и мы хотел, чтобы return в таких конструкциях работал так же, как return в Java synchronized блоке или for цикле. - person yole; 25.10.2019