Ответ 1
Call-by-Name: = > Тип
Обозначение => Type
означает вызов по имени, который является одним из параметров many ways. Если вы не знакомы с ними, я рекомендую потратить некоторое время на то, чтобы прочитать эту статью в Википедии, хотя в настоящее время это, главным образом, призыв к значению и вызов по ссылке.
Что это значит, то, что передано, заменяется именем значения внутри функции. Например, возьмите эту функцию:
def f(x: => Int) = x * x
Если я назову это так
var y = 0
f { y += 1; y }
Затем код будет выполняться следующим образом
{ y += 1; y } * { y += 1; y }
Хотя это поднимает вопрос о том, что происходит, если существует столкновение имени идентификатора. В традиционном вызове по имени используется механизм, называемый замещением захвата, чтобы избежать столкновений имен. Однако в Scala это реализовано по-другому с тем же результатом - имена идентификаторов внутри параметра не могут ссылаться или теневые идентификаторы в вызываемой функции.
Есть несколько других вопросов, связанных с вызовом по имени, о котором я буду говорить после объяснения двух других.
0-arity Функции:() = > Тип
Синтаксис () => Type
обозначает тип a Function0
. То есть, функция, которая не принимает никаких параметров и что-то возвращает. Это эквивалентно, например, вызову метода size()
- он не принимает никаких параметров и возвращает число.
Интересно, однако, что этот синтаксис очень похож на синтаксис для анонимного литерала функции, что является причиной некоторой путаницы. Например,
() => println("I'm an anonymous function")
является анонимным литералом функции arity 0, тип которого
() => Unit
Итак, мы могли бы написать:
val f: () => Unit = () => println("I'm an anonymous function")
Однако важно не путать тип со значением.
Единица = > Тип
На самом деле это просто Function1
, первый параметр которого имеет тип Unit
. Другими способами написать это будет (Unit) => Type
или Function1[Unit, Type]
. Дело в том, что это вряд ли когда-либо будет тем, что нужно. Основной целью Unit
является указание значения, которое не интересует, поэтому не имеет смысла получать это значение.
Рассмотрим, например,
def f(x: Unit) = ...
Что можно сделать с x
? Он может иметь только одно значение, поэтому его не нужно получать. Одним из возможных вариантов использования будет функция цепочки, возвращающая Unit
:
val f = (x: Unit) => println("I'm f")
val g = (x: Unit) => println("I'm g")
val h = f andThen g
Поскольку andThen
определяется только на Function1
, а функции, которые мы цепочки, возвращаем Unit
, мы должны были определить их как тип типа Function1[Unit, Unit]
, чтобы иметь возможность их связывать.
Источники путаницы
Первым источником путаницы является то, что сходство между типом и литералом, которое существует для функций 0-arity, также существует для вызова по имени. Другими словами, думая, что, поскольку
() => { println("Hi!") }
является литералом для () => Unit
, тогда
{ println("Hi!") }
будет литералом для => Unit
. Это не. Это блок кода, а не буквальный.
Другим источником путаницы является то, что значение типа Unit
записывается ()
, которое выглядит как список параметров 0-arity (но это не так).