Call-by-Name: => Тип
=> Type
Обозначение означает-имя-вызов по, который является одним из многих способов , параметры могут быть переданы. Если вы с ними не знакомы, я рекомендую потратить некоторое время на чтение этой статьи в Википедии, хотя в настоящее время это в основном вызов по значению и вызов по ссылке.
Это означает, что то, что передается, заменяется именем значения внутри функции. Например, возьмите эту функцию:
def f(x: => Int) = x * x
Если я назову это так
var y = 0
f { y += 1; y }
Тогда код будет выполняться так
{ y += 1; y } * { y += 1; y }
Хотя это поднимает вопрос о том, что происходит, если есть столкновение имени идентификатора. В традиционном вызове по имени используется механизм, называемый заменой, избегающей захвата, чтобы избежать конфликта имен. Однако в Scala это реализовано другим способом с тем же результатом - имена идентификаторов внутри параметра не могут ссылаться или идентификаторы теней в вызываемой функции.
Есть несколько других моментов, связанных с именами, о которых я расскажу после объяснения двух других.
Функции 0-арности: () => Тип
Синтаксис () => Type
обозначает тип Function0
. То есть функция, которая не принимает параметров и что-то возвращает. Это эквивалентно, скажем, вызову метода size()
- он не принимает параметров и возвращает число.
Интересно, однако, что этот синтаксис очень похож на синтаксис литерала анонимной функции , что является причиной некоторой путаницы. Например,
() => println("I'm an anonymous function")
является литералом анонимной функции arity 0, тип которого
() => Unit
Таким образом, мы могли бы написать:
val f: () => Unit = () => println("I'm an anonymous function")
Однако важно не путать тип со значением.
Unit => Type
Это на самом деле просто Function1
, чей первый параметр имеет тип Unit
. Другие способы написать это было бы (Unit) => Type
или Function1[Unit, Type]
. Дело в том, что ... вряд ли это когда-нибудь будет тем, чего ты хочешь. В Unit
основной цели Type является указание на значение один не интересуют, поэтому не имеет смысла , чтобы получить это значение.
Рассмотрим, например,
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-арности, также существует для вызова по имени. Другими словами, думая, что, потому что
() => { println("Hi!") }
это буквально для () => Unit
, то
{ println("Hi!") }
было бы буквально для => Unit
. Это не. Это блок кода , а не литерал.
Другим источником путаницы является то, Unit
что записано значение типа ()
, которое выглядит как список параметров 0-арности (но это не так).
case class Scheduled(time: Int)(callback: => Unit)
. Это работает, потому что список вторичных параметров не предоставляется публично и не включается в сгенерированныеequals
/hashCode
методы.