В чем разница между =>, () = > и=>


Я пытаюсь представить функцию, которая не принимает аргументов и не возвращает значения (я имитирую функцию 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 133

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 обозначает тип a 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-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 не блокирует.