Типы и методы метода Scala в качестве параметров - PullRequest
6 голосов
/ 07 октября 2011

В следующем примере кода я не понимаю, почему функцию fun можно передать в качестве аргумента методу addAction.Метод fun имеет тип Unit, а метод addAction ожидает функцию типа () => Unit.

Если fun имеет тип () => Unit, то почему компилятор жалуется, что fun имеет тип Unit, когда я пытаюсь добавить fun в список действий: actions = fun :: actions?

package myscala

object MyScala {

  def fun() { println("fun1 executed.") }

  def addAction(a: () => Unit) {
    actions = a :: actions
  }

  var actions: List[() => Unit] = List()

  def main(args: Array[String]) {
    // the following line would produce a compiler error (found: Unit, required: () => Unit), it's OK
    // actions = fun :: actions
    actions = (() => fun) :: actions // OK
    // I would expect the same compiler error here (found: Unit, required: () => Unit), but it's OK why?
    addAction(fun)
    actions.foreach(_()) // prints twice "fun1 executed"
  }
}

Ответы [ 3 ]

8 голосов
/ 07 октября 2011

Возьмем это как вводный пример:

def fun() { println("fun1 executed.") }

val a1 = fun
val a2: () => Unit = fun

Обе строки компилируются и (благодаря выводу типа) они выглядят эквивалентно.Однако a1 имеет тип Unit, в то время как a2 имеет тип () => Unit ... Как это возможно?

Поскольку вы явно не предоставляете тип a1, компиляторы интерпретируют fun как метод fun вызов типа Unit, следовательно, тип a1 совпадает с типом fun.Это также означает, что в этой строке будет напечатано fun1 выполнено.

Однако a2 явно объявил тип () => Unit.Здесь вам поможет компилятор, и он понимает, что поскольку для контекста требуется функция типа () => Unit, а вы предоставили метод, соответствующий этому типу, он не должен вызывать этот метод, а обрабатывать его как функцию первого класса!

Вы не обречены явно указывать тип a1.Говоря:

val a1 = fun _

Теперь вы понимаете, где ваша проблема?

5 голосов
/ 07 октября 2011

Вам нужно написать fun _ в первом случае, чтобы избежать вызова метода и выполнения вместо него eta-раскрытия.

Это будет работать:

actions = (fun _) :: actions

Если вы этого не сделаетепосле этого оценивается fun.

Подробнее см. в разделе 6.7 ( Значения метода ) Справочник по языку Scala .

Что касается того, почему fun не оценивается во втором случае, то это потому, что вывод типа может ясно сделать вывод, что addAction ожидает функцию.Кстати, технически тип fun - это ()Unit, а не Unit, то есть тип метода, а не тип значения.См. Раздел 3.3.1 в справочнике для получения дополнительной информации.

3 голосов
/ 07 октября 2011

Существует разница между методами и функциями. В вашем случае actions - это список функций. Когда компилятор знает, что требуется функция (как в случае addAction), он может автоматически преобразовать метод fun в функцию. Теперь :: также является методом, поэтому компилятор также знает, что он принимает функции в качестве параметров. Но проблема в синтаксическом сахаре правоассоциативного оператора ::. Если бы вы назвали его как метод: actions.::(fun) он скомпилируется (хотя я не могу проверить это в данный момент). При написании fun :: actions компилятор думает, что fun является выражением, и поэтому оценивает его, и, поскольку он «возвращает» Unit, вы получаете ошибку компилятора.

EDIT

Поскольку теперь у меня есть возможность проверить свою гипотезу (которая была неверна), вот ваши варианты:

// Usual syntax
actions.::[() => Unit](fun)
actions.::(fun: () => Unit)
actions.::(fun _)
// Operator syntax
(fun: () => Unit) :: actions
(fun _) :: actions
...