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методи.