Некоторые вопросы о разнице между вызовами по имени и функциям 0-arity

Здесь есть некоторые дискуссии, но у меня есть некоторые конкретные вопросы, на которые я не мог найти ответ. Итак, по вызову по имени, я имею в виду тип =>T, а по функции 0-arity я имею в виду () => T

Я понимаю (я думаю) концептуальную разницу, но, вероятно, мне что-то не хватает, поскольку у меня еще много вопросов:

  • Почему у нас есть понятие =>T вообще, если мы всегда можем использовать () => T?
  • Есть ли какие-либо синтаксические/функциональные ограничения для каждого? На данный момент я обнаружил, что => cannnot нельзя использовать как поле класса. Это единственное ограничение?
  • Является ли сгенерированный код одинаковым для обоих?
  • Должен ли я всегда предпочитать =>T? И почему?
  • Правильно ли вызывать =>T тип? Он выглядит так, как будто у него нет никакого представления типа в scala.

Ответ 1

1) Это более удобно использовать, особенно внутри DSL:

def printAndGet[T](f: => T) = {
  val res = f
  println(res + " printed") 
  res
}

scala> :paste
// Entering paste mode (ctrl-D to finish)

val k = printAndGet {
  val a = 5
  5 * a 
}

// Exiting paste mode, now interpreting.

25 printed
k: Int = 25

2) => T может быть только параметром метода или функции. И фактически => T и () => T не взаимозаменяемы:

scala> def aaa(f: => String) = f
aaa: (f: => String)String

scala> val a: Function1[() => String, String] = aaa _
<console>:8: error: type mismatch;
 found   : (=> String) => String
 required: (() => String) => String
       val a: Function1[() => String, String] = aaa _
                                                ^

Благодаря @som-snytt, сделайте следующее:

scala> object O { def f(i: Int) = i; def f(i: => Int) = i + 1 }
defined object O

scala> O.f(5)
res12: Int = 5

scala> O.f(5: (=> Int))
<console>:1: error: no by-name parameter type allowed here
       O.f(5: (=> Int))
           ^

Даже это должно работать, если оно компилируется, но оно не работает (scala 2.11.2, 2.11.5 REPL просто сбой):

scala> val k: (=> Int) => Int = O.f _
k: (=> Int) => Int = <function1>

scala> k(5) //should be 6
res18: Int = 5 //WTF?

Последний выглядит как ошибка

3) Не совсем, если вы хотите того же, просто преобразуйте => T в () => T:

scala> def aaa(f: => String) = {f _}
aaa: (f: => String)() => String

Bytecode также может отличаться. Например, компилятор будет скорее иметь встроенный код из => T без генерации лямбда для него. Итак, главное отличие состоит в том, что () => T на самом деле является объектом (гражданин первого класса), => T не является.

4) см. 1, но иногда вам может потребоваться убедиться, что пользователь знает, что вычисление может быть отложено - () => T лучше.

5) Это часть сигнатуры типа, просто посмотрите на eta-расширение:

scala> def aaa(f: => String) = {f}
aaa: (f: => String)String

scala> aaa _ //convert method into a function
res7: (=> String) => String = <function1>

scala> val a: ( => String) =>  String = aaa _
a: (=> String) => String = <function1>

Однако scala не распознает его как независимый тип:

scala> val a: Function1[( => String), String] = aaa _
<console>:1: error: no by-name parameter type allowed here
       val a: Function1[( => String), String] = aaa _
                          ^