2011-02-07 1 views
7

Дано:Частичное применение функции преждевременно работает кодоблок при использовании подчеркивание

def save(f: => Any)(run:Boolean) { if (run) { println("running f"); f } else println("not running f") } 

Я могу назвать его:

save("test")(true) -> running f 
save("test")(false) -> not running f 
save(throw new RuntimeException("boom!"))(false) -> not running f 
save(throw new RuntimeException("boom!"))(true) -> running f and then exception thrown 

Вот любопытное поведение с частичным применением:

save(throw new RuntimeException("boom!"))(_) -> (Boolean) => Unit = <function1> //as expected 
save(throw new RuntimeException("boom!")) _ -> exception thrown 

Кодовый блок сразу оценивается без передачи в качестве функции. В чем разница между вышеуказанными 2 заявлениями?

+1

Вы можете найти несколько объяснений здесь: http://stackoverflow.com/questions/2363013/in-scala-why-cant-i-partially-apply-a-function-without-explicitly-specifying-it –

+3

IMHO, это ошибка. –

ответ

2

Первый случай,

save(throw new RuntimeException("boom!")) _ 

В соответствии с "Scala Reference" (§6.7), задний подчеркивание используется вместо списка аргументов, и выражение преобразуется в

val f: (Boolean) => Unit = save(throw new RuntimeException("boom!")) 

где первый аргумент def save немедленно оценивается.

Выражение е _ хорошо сформированным, если е имеет тип метода или если е является вызова по имени параметра. Если e - это метод с параметрами, то e_ представляет e, преобразованный в тип функции с помощью расширения eta (§6.26.5). Если e - это безпараметрический метод или позывной по имени параметр type => T, e_ представляет функцию типа() => T, которая оценивает e, когда она применяется к пустым параметрическим спискам () ,

Чтобы сделать вещи работать, как вы ожидаете, некоторые изменения необходимы:

scala> def save(f:() => Any)(run:Boolean) { if (run) { println("running f"); f() } else println("not running f") } 
save: (f:() => Any)(run: Boolean)Unit 

scala> val f = save(() => throw new RuntimeException("boom!")) _ 
f: (Boolean) => Unit = <function1> 

scala> f(true) 
running f 
java.lang.RuntimeException: boom! 
     at $anonfun$1.apply(<console>:6) 

Второй случай,

save(throw new RuntimeException("boom!"))(_) 

Согласно "Scala Reference" (§6.23), когда заполнитель используется в качестве замены аргумента, выражение преобразуется в

val f: (Boolean) => Unit = save(throw new RuntimeException("boom!"))(_) 
+1

Действительно, согласно приведенной спецификации, я ожидаю, что вызов по имени будет работать. В конце концов, в этом случае 'e' оценивается только при применении к пустому списку параметров. –

+0

Я согласен с Даниэлем. Если в спецификации указано, что вызов преобразуется в() => T, то функция не будет оцениваться до тех пор, пока она не будет применена. – ssanj

0

В настоящее время рассматривается поведение параметров по одному имени при расширении eta, см. this bug. Ваш код работает так, как вы ожидаете (т. Е. Строка save(throw new RuntimeException("boom!")) _ возвращает функцию без исключения) с недавними ночными сборками в 2.10. Посмотрим, останется ли он до выпуска!

См. Также this question относительно связанного вопроса в общем случае расширения eta, не включающего вызов по имени.

Смежные вопросы