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-arity: () => Тип
Синтаксис () => Type
означає тип a Function0
. Тобто функція, яка не приймає жодних параметрів і щось повертає. Це еквівалентно, скажімо, виклику методу size()
- він не приймає жодних параметрів і повертає число.
Однак цікаво, що цей синтаксис дуже схожий на синтаксис анонімної функції літерал , що є причиною певної плутанини. Наприклад,
() => println("I'm an anonymous function")
- це анонімна функція, буквальна ариті 0, тип якої
() => Unit
Тож ми могли написати:
val f: () => Unit = () => println("I'm an anonymous function")
Важливо не плутати тип зі значенням.
Одиниця => Тип
Це насправді просто а 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-arity, існує також для виклику по імені. Іншими словами, думаючи, що, тому що
() => { println("Hi!") }
то є буквальним для () => Unit
, то
{ println("Hi!") }
було б буквальним для => Unit
. Це не. Це блок коду , а не буквальний.
Іншим джерелом плутанини є те, що записано значенняUnit
типу , яке виглядає як список параметрів 0-arity (але це не так).()
case class Scheduled(time: Int)(callback: => Unit)
. Це працює, тому що список вторинних параметрів не піддається загальному опроміненню, а також не включається в створеніequals
/hashCode
методи.