Практическое различие между 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. (По крайней мере, я ДУМАЮ, что то, что происходит, пожалуйста, поправьте меня, если я ошибаюсь). Однако это все мои собственные предположения. Там наверняка будет какая-то дополнительная магия компилятора, которая будет проходить под ним, чтобы обеспечить совместимость методов и функций.