Scala Call By Name путаница - PullRequest
       4

Scala Call By Name путаница

6 голосов
/ 13 февраля 2011

Я работаю над некоторыми примерами вызовов по имени, используя REPL и запускаю те же примеры в Eclipse.

Вот что в Eclipse:
Сценарий 1:

val funct = {println("Calling funct")}
takesFunct(funct)

def takesFunct(f: => Unit)
{
   val b = f
}

Вывод: Calling funct

Сценарий 2:
метод принимаеттот же

takesFunct({println("Calling funct")}

Вывод:
Функция вызова
Функция вызова

Scala REPL Сценарий 1:

scala> def takesFunct(f: => Unit)
{
  val b = f
}
takesFunct: (f: => Unit)Unit

scala> val funct = {println("Calling funct")}
Calling funct
funct: Unit = ()

scala> takesFunct(funct)
// No Output

Сценарий 2 Тот же метод, что иопределено выше

scala> takesFunct({println("Calling funct")}
Calling funct

Два вопроса
1) Почему выходные данные Eclipse отличаются от REPL?
2) В чем разница между передачей в

val funct = {...}
takesFunct(funct)

в отличие от

takesFunct({...})

Ответы [ 3 ]

9 голосов
/ 13 февраля 2011

Обновлено после ответа @ IttayD:

Сценарий 1 на Eclipse правильный, вы поймете, почему ниже.Сценарий 2 - явно ошибка Eclipse.ScalaIDE на Eclipse известен своей сломленностью.Я бы не стал доверять (или использовать это).При необходимости используйте плагин Intellij IDEA Scala.

Ответ на оба ваших вопроса заключается в том, что {} - это блок, который возвращает тип возвращаемого значения последнего оператора.Это точно так же, как схема (begin) или Common Lisp (progn).Когда у вас есть:

scala> def takesFunct(f: => Unit)
{
  val b = f
}
takesFunct: (f: => Unit)Unit

scala> val funct = {println("Calling funct")}
Calling funct
funct: Unit = ()

scala> takesFunct(funct)
// No Output

funct, RHS уже была с нетерпением оценена и вернула значение () типа Unit в функцию.Применение уже вычисленного значения к функции вызова по имени и использование его в теле не вызывает переоценки, поскольку значение уже является листом.

Дальнейшее обновление:

def takesFunct(f: => Unit)

имеет по существу ту же семантику, что и

def takesFunct(f: () => Unit)

, которая известна как потоковая или отложенная оценка в определенных кругах.Есть одно существенное отличие, которое заключается в том, как вы вызываете предоставленный аргумент.В последнем случае, чтобы получить значение из f, вы должны вызвать его как таковое - то есть f().В первом случае f является ленивым выражением , которое оценивается как значение, когда оно впервые упоминается как таковое, следовательно, call-by-name .Вы можете думать о синтаксисе f: => Unit как о способе автоматической упаковки любого выражения, которое вы предоставляете в контейнере {}.Содержимое которого извлекается при использовании следующим образом:

scala> val a = { 1 } // 1 wrapped in {}, and retrieved when assigned to a
a: Int = 1

А как насчет этого?

scala> takesFunct({println("Calling funct")})
Calling funct

Это потому, что теперь вы создаете блок на месте, который привязан кпараметр функции f, и он оценивается только при использовании его в val b = f.Давайте сделаем еще один эксперимент:

scala> takesFunct(println("Calling funct"))
Calling funct

Почему вы спрашиваете?Потому что println(...) был обернут в {}, который связан с f.Ссылка f извлекает значение внутри контейнера, которое равно println(...), то есть ():Unit.В предыдущем примере f был связан с { { println(...) } }, что совпадает с { println(...) }, поэтому вы получите тот же результат.На самом деле вы можете вкладывать {} на неопределенный срок и все равно получать обратно одно и то же.Единственное отличие состоит в том, что ручное указание {} позволяет вам помещать несколько операторов внутри так:

scala> takesFunct({ println("hello"); println("world") })
hello
world

Надеюсь, это поможет.

2 голосов
/ 14 февраля 2011

Нет разницы в выходе.Разница в том, что вы хотите.Из Eclipse вы запустили две вещи:

val funct = {println("Calling funct")} // prints Calling funct here
takesFunct(funct)

def takesFunct(f: => Unit)
{
   val b = f
}

и

val funct = {println("Calling funct")} // prints Calling funct here
takesFunct({println("Calling funct")}

def takesFunct(f: => Unit)
{
   val b = f                           // prints Calling funct here
}

На REPL то же самое произошло с вашими собственными журналами:

scala> def takesFunct(f: => Unit)
{
  val b = f
}
takesFunct: (f: => Unit)Unit

scala> val funct = {println("Calling funct")}
Calling funct
funct: Unit = ()

scala> takesFunct(funct)
// No Output

Сейчасво втором сценарии все, что вы сделали, это:

scala> takesFunct({println("Calling funct")}
Calling funct

Поскольку вы не повторяли назначение val funct, которое все еще присутствовало во втором сценарии Eclipse, оно не печатало сообщение.

Обратите внимание, что

val funct = {println("Calling funct")}

с практической точки зрения эквивалентно

println("Calling funct")
val funct = ()
0 голосов
/ 13 февраля 2011

Сценарий 1: Как и ожидалось. Вывод из первой строки, без вызова метода.

Сценарий 2: у меня нет Eclipse, но звучит неправильно.

REPL Сценарий 1: невозможно определить вызов по значению имени. то, что вы сделали, это присвоили последнему значению выражения {println("Calling funct")} функцию. Это последнее значение является результатом println, который является Unit. Так что takeFunct получает thunk, который оценивается как просто Unit, так что ничего не печатается.

REPL Сценарий 2: Как и ожидалось

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...