Практическое различие между def f (x: Int) = x + 1 и val f = (x: Int) => x + 1 в Scala
Я новичок в Scala, и у меня возникла проблема с пониманием этого. Почему существуют два синтаксиса для одной и той же концепции, и ни один из них не является более эффективным или коротким в этом (просто с точки зрения ввода, может быть, они отличаются поведением - вот что я прошу).
В Go аналоги имеют практическую разницу - вы не можете перенаправить ссылку на лямбду, назначенную переменной, но вы можете ссылаться на именованную функцию из любого места. Scala смешивает эти два, если я правильно их понимаю: вы можете перенаправить ссылку на любую переменную (пожалуйста, поправьте меня, если я ошибаюсь).
Обратите внимание, что этот вопрос не является дубликатом В чем разница между "def" и "val" для определения функции.
Я знаю, что def
оценивает выражение после =
каждый раз, когда он ссылается/вызывается, а val
- только один раз. Но это отличается от того, что выражение в определении val
оценивается функцией.
Это также не дубликат Функции против методов в Scala.
Этот вопрос касается синтаксиса Scala, и он не спрашивает о разнице между функциями и методами напрямую. Несмотря на то, что ответы могут быть похожими по содержанию, все равно важно, чтобы эта точная точка была очищена на этом сайте.
Ответы
Ответ 1
Существуют три основных отличия (которые я знаю):
1. Внутреннее представление
Функциональные выражения (как анонимные функции или lambdas) представлены в сгенерированном байт-коде как экземпляры любого из признаков Function
. Это означает, что выражения функций также являются объектами. Определения методов, с другой стороны, являются гражданами первого класса в JVM и имеют специальное представление байт-кода. Как это влияет на производительность, сложно сказать без профилирования.
2. Синтаксис ссылки
Ссылки на функции и методы имеют разные синтаксисы. Вы не можете просто сказать foo
, когда хотите отправить ссылку метода в качестве аргумента в какую-либо другую часть вашего кода. Вы должны сказать foo _
. С функциями вы можете просто сказать foo
, и все будет работать по назначению. Синтаксис foo _
эффективно завершает вызов foo
внутри анонимной функции.
3. Поддержка дженериков
Способы поддержки параметров типа, функции нет. Например, нет возможности выразить следующее, используя значение функции:
def identity[A](a: A): A = a
Ближайшим будет это, но он теряет информацию о типе:
val identity = (a: Any) => a
Ответ 2
В качестве расширения к первой точке Ionut, возможно, стоит взглянуть на http://www.scala-lang.org/api/current/#scala.Function1.
Из моего понимания, экземпляр функции, описанной вами (т.е.
val f = (x: Int) => x + 1
) расширяет класс Function1
. Последствия этого заключаются в том, что экземпляр функции потребляет больше памяти, чем определение метода. Методы являются врожденными для JVM, поэтому их можно определить во время компиляции. Очевидная стоимость функции - это потребление памяти, но при этом появляются дополнительные преимущества, такие как композиция с другими объектами Function.
Если я правильно понимаю, причина def
и lambdas могут работать вместе, потому что класс Function имеет тип-тип (T1) ⇒ R
, который подразумевается его методом apply()
https://github.com/scala/scala/blob/v2.11.8/src/library/scala/Function1.scala#L36. (По крайней мере, я ДУМАЮ, что то, что происходит, пожалуйста, поправьте меня, если я ошибаюсь). Однако это все мои собственные предположения. Там наверняка будет какая-то дополнительная магия компилятора, которая будет проходить под ним, чтобы обеспечить совместимость методов и функций.