Call-by-Name: => Тип
Обозначение => Type
расшифровывается как call-by-name, который является одним из многих способов передачи параметров .Если вы не знакомы с ними, я рекомендую потратить некоторое время на чтение этой статьи в Википедии, хотя в настоящее время это в основном вызов по значению и вызов по ссылке.
Что это означает, что подставлено для имени значения внутри функции.Например, возьмите эту функцию:
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")
является литералом анонимной функции арности 0, чей тип равен
() => Unit
Таким образом, мы могли бы написать:
val f: () => Unit = () => println("I'm an anonymous function")
Однако важно не путать тип со значением.
Unit => Type
На самом деле это просто 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-арности.существует для вызова по имени.Другими словами, если подумать, что, поскольку
() => { println("Hi!") }
является литералом для () => Unit
, то
{ println("Hi!") }
будет литералом для => Unit
.Это не.Это блок кода , а не литерал.
Еще одним источником путаницы является то, что Unit
значение типа записано ()
, которое выглядит как список параметров 0-арности (но это не так).