В чем разница между =>, () = > и=>
Я пытаюсь представить функцию, которая не принимает аргументов и не возвращает значения (я имитирую функцию 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") )
дайте ребенку его бутылку!
3 ответа:
Call-by-Name: = > Type
The
=> Type
обозначение означает вызов по имени, который является одним из много способов параметры могут быть переданы. Если вы не знакомы с ними, я рекомендую потратить некоторое время, чтобы прочитать эту статью в Википедии, хотя в настоящее время это в основном call-by-value и Call-by-reference.это означает, что то, что передается заменить для имени значения внутри функции. Например, возьмите это функция:
def f(x: => Int) = x * x
если я называю это
var y = 0 f { y += 1; y }
тогда код будет выполняться следующим образом
{ y += 1; y } * { y += 1; y }
хотя это поднимает вопрос о том, что происходит, если есть столкновение имен идентификаторов. В традиционном вызове по имени происходит механизм, называемый capture-avoiding substitution, чтобы избежать конфликтов имен. В Scala, однако, это реализовано по-другому с тем же результатом -- имена идентификаторов внутри параметра не могут ссылаться или тень идентификаторы в вызываемой функции.
есть некоторые другие моменты, связанные с позывным, о которых я буду говорить после объяснения двух других.
0-функции арности: () = > тип
синтаксис
() => Type
обозначает тип aFunction0
. То есть, функция, которая не принимает никаких параметров и возвращает что-то. Это эквивалентно, скажем, вызову метода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-arity, также существует для вызова по имени. Другими словами, думая, что, потому что
() => { println("Hi!") }
- это дословный для
() => Unit
, потом{ println("Hi!") }
было бы буквальным для
=> Unit
. Это не. То есть блок кода, а не буквальное.еще один источник путаницы в том, что
Unit
типа стоимостью написано()
, который выглядит как список параметров 0-arity (но это не так).
case class Scheduled(time : Int, callback : => Unit)
The
case
модификатор делает неявноеval
из каждого аргумента в конструктор. Следовательно (как кто-то заметил), Если вы удалитеcase
вы можете использовать параметр вызова по имени. Компилятор, вероятно, может позволить это в любом случае, но это может удивить людей, если он создалval callback
вместо превращения вlazy val callback
.при изменении
callback: () => Unit
теперь ваш случай просто принимает функцию, а не параметр вызова по имени. Очевидно, что функция может быть сохранена вval callback
так что нет никаких проблем.самый простой способ получить то, что вы хотите (
Scheduled(40, println("x") )
где параметр call-by-name используется для передачи лямбда), вероятно, пропуститьcase
и явно создатьapply
что вы не могли получить в первую очередь:class Scheduled(val time: Int, val callback: () => Unit) { def doit = callback() } object Scheduled { def apply(time: Int, callback: => Unit) = new Scheduled(time, { () => callback }) }
используется:
scala> Scheduled(1234, println("x")) res0: Scheduled = Scheduled@5eb10190 scala> Scheduled(1234, println("x")).doit x
в вопросе вы хотите имитировать функцию SetTimeOut в JavaScript. Основываясь на предыдущих ответах, я пишу следующий код:
class Scheduled(time: Int, cb: => Unit) { private def runCb = cb } object Scheduled { def apply(time: Int, cb: => Unit) = { val instance = new Scheduled(time, cb) Thread.sleep(time*1000) instance.runCb } }
в REPL, мы можем получить что-то вроде этого:
scala> Scheduled(10, println("a")); Scheduled(1, println("b")) a b
наше моделирование не ведет себя точно так же, как SetTimeOut, потому что наше моделирование блокирует функцию, но SetTimeOut не блокирует.