Я пытаюсь представить функцию, которая не принимает аргументов и не возвращает значения (я имитирую функцию setTimeout в JavaScript, если вы должны знать.)
case class Scheduled(time : Int, callback : => Unit)
не компилируется, говоря: "параметры val не могут быть названы по имени"
case class Scheduled(time : Int, callback : () => Unit)
компилируется, но должен вызываться странным образом, а не
Scheduled(40, { println("x") } )
Я должен сделать это
Scheduled(40, { () => println("x") } )
Что также работает
class Scheduled(time : Int, callback : Unit => Unit)
но вызывается еще менее разумным способом
Scheduled(40, { x : Unit => println("x") } )
(Какой будет переменная типа Unit?) Конечно, мне нужен конструктор, который можно вызывать так, как я бы его вызывал, если бы это была обычная функция:
Scheduled(40, println("x") )
Дай ребенку свою бутылочку!
case class Scheduled(time: Int)(callback: => Unit)
. Это работает, потому что список вторичных параметров не предоставляется публично и не включается в сгенерированныеequals
/hashCode
методы.Ответы:
Call-by-Name: => Тип
=> Type
Обозначение означает-имя-вызов по, который является одним из многих способов , параметры могут быть переданы. Если вы с ними не знакомы, я рекомендую потратить некоторое время на чтение этой статьи в Википедии, хотя в настоящее время это в основном вызов по значению и вызов по ссылке.Это означает, что то, что передается, заменяется именем значения внутри функции. Например, возьмите эту функцию:
Если я назову это так
Тогда код будет выполняться так
Хотя это поднимает вопрос о том, что происходит, если есть столкновение имени идентификатора. В традиционном вызове по имени используется механизм, называемый заменой, избегающей захвата, чтобы избежать конфликта имен. Однако в Scala это реализовано другим способом с тем же результатом - имена идентификаторов внутри параметра не могут ссылаться или идентификаторы теней в вызываемой функции.
Есть несколько других моментов, связанных с именами, о которых я расскажу после объяснения двух других.
Функции 0-арности: () => Тип
Синтаксис
() => Type
обозначает типFunction0
. То есть функция, которая не принимает параметров и что-то возвращает. Это эквивалентно, скажем, вызову методаsize()
- он не принимает параметров и возвращает число.Интересно, однако, что этот синтаксис очень похож на синтаксис литерала анонимной функции , что является причиной некоторой путаницы. Например,
является литералом анонимной функции arity 0, тип которого
Таким образом, мы могли бы написать:
Однако важно не путать тип со значением.
Unit => Type
Это на самом деле просто
Function1
, чей первый параметр имеет типUnit
. Другие способы написать это было бы(Unit) => Type
илиFunction1[Unit, Type]
. Дело в том, что ... вряд ли это когда-нибудь будет тем, чего ты хочешь. ВUnit
основной цели Type является указание на значение один не интересуют, поэтому не имеет смысла , чтобы получить это значение.Рассмотрим, например,
Что можно было сделать с
x
? Он может иметь только одно значение, поэтому его не нужно получать. Одним из возможных применений было бы возвращение функций цепочкиUnit
:Поскольку
andThen
он определен толькоFunction1
, а функции, которые мы объединяем, возвращаютсяUnit
, мы должны были определить их как типы,Function1[Unit, Unit]
чтобы иметь возможность их связывать .Источники путаницы
Первым источником путаницы является то, что сходство между типом и литералом, которое существует для функций 0-арности, также существует для вызова по имени. Другими словами, думая, что, потому что
это буквально для
() => Unit
, тобыло бы буквально для
=> Unit
. Это не. Это блок кода , а не литерал.Другим источником путаницы является то,
Unit
что записано значение типа()
, которое выглядит как список параметров 0-арности (но это не так).источник
case ... =>
, поэтому я не упомянул об этом. Грустно, но правда. :-)1
, символ'a'
, строка"abc"
или функция() => println("here")
, в некоторых примерах). Его можно передавать в качестве аргумента, хранить в переменных и т. Д. «Блок кода» - это синтаксическое разграничение операторов - это не значение, его нельзя передать или что-то в этом роде.(Unit) => Type
против() => Type
- первый - этоFunction1[Unit, Type]
, а второй -Function0[Type]
.case
Модификатор делает неявноеval
из каждого аргумента конструктора. Следовательно (как кто-то заметил), если вы удалите,case
вы можете использовать параметр call-by-name. Компилятор, возможно, в любом случае мог бы разрешить это, но он мог бы удивить людей, если бы он создал,val callback
а не превратился вlazy val callback
.Когда вы переходите на
callback: () => Unit
сейчас, ваш случай просто принимает функцию, а не параметр call-by-name. Очевидно, что функция может быть сохранена,val callback
так что нет проблем.Самый простой способ получить то, что вы хотите (
Scheduled(40, println("x") )
где параметр call-by-name используется для передачи лямбды), это, вероятно, пропуститьcase
и явно создать то,apply
что вы не могли получить в первую очередь:В использовании:
источник
В вопросе вы хотите смоделировать функцию SetTimeOut в JavaScript. Основываясь на предыдущих ответах, я пишу следующий код:
В REPL мы можем получить что-то вроде этого:
Наше моделирование не ведет себя точно так же, как SetTimeOut, потому что наше моделирование является функцией блокировки, но SetTimeOut не является блокирующим.
источник
Я делаю это так (просто не хочу нарушать правила):
и назовите это
источник