Странные вещи с каррированной функцией

У меня странная ситуация, которую я не понимаю. Я читаю книгу "Программирование на Scala", гл. 9.

Скажем, у меня есть каррированная функция:

def withThis(n:Int)(op:Int=>Unit){
      println("Before")
      op(n);
      println("After")
}

Когда я вызываю его с одним аргументом внутри специального фигурного синтаксиса, он работает так, как ожидалось:

withThis(5){
   (x) => {println("Hello!"); println(x); }
}
// Outputs
Before
Hello!
5
After

Однако, если я поставлю два утверждения, я получу что-то странное:

withThis(5){
     println("Hello!")
     println(_)
}
// Outputs
Hello!
Before
5
After

Как получилось "Привет!" печатается перед «До», а затем внутри печатается «5»? Я сумасшедший?


person Andriy Drozdyuk    schedule 28.04.2011    source источник


Ответы (2)


Ваш последний пример кода должен быть переписан следующим образом, чтобы получить ожидаемый результат:

withThis(5) { x =>
     println("Hello!")
     println(x)
}

В противном случае ваш пример эквивалентен

withThis(5) {
     println("Hello!")
     (x: Int) => println(x)
}

так как заполнитель _ будет расширен, чтобы связать как можно более тесно недегенеративным способом (т. е. он не будет расширяться до println(x => x)).

Следует также отметить, что блок всегда возвращает свое последнее значение. В вашем примере последнее значение на самом деле (x: Int) => println(x).

person Jean-Philippe Pellet    schedule 28.04.2011
comment
Но println(x => x) даже не правильный синтаксис. В любом случае - я понимаю, что делает println(_) - я больше запутался в этом блоке. - person Andriy Drozdyuk; 28.04.2011
comment
Синтаксис правильный. В этом случае он не будет компилироваться из-за отсутствующего типа параметра, но будет работать, если компилятор сможет его определить. Простой пример: def doit(f: Int => Int) = (); doit(x => x) - person Jean-Philippe Pellet; 28.04.2011
comment
Я знаю, что это эквивалентно (x: Int) => println(x), мой вопрос в том, почему сначала выполняется println. Я думаю, вы ответили, что блок всегда возвращает последнее значение. Означает ли это, что блоки не лениво оцениваются? - person Andriy Drozdyuk; 02.05.2011
comment
Вы правы, в этом контексте блоки не лениво оцениваются. - person Jean-Philippe Pellet; 03.05.2011

Во втором примере часть в завитках: { println("Hello!"); println(_) } - это блок, который печатает "Hello!" и возвращает карри println. Представьте, что это упрощенно, как { println("Hello!"); 5 }, блок, который печатает "Hello!" и возвращает 5.

person Ben Jackson    schedule 28.04.2011
comment
Я не думаю, что я получил часть блоков в книге еще. Вот поэтому я и запутался, наверное. - person Andriy Drozdyuk; 28.04.2011
comment
Значит, нет способа заставить его работать так, как ожидалось, во втором примере? Я пробовал с параметрами по имени, но они не принимают аргумент. - person Andriy Drozdyuk; 28.04.2011
comment
Просто чтобы быть придирчивым: блок возвращает не «каррированное println», а анонимную функцию с одним аргументом, которая вызывает println с полученным аргументом. - person Jean-Philippe Pellet; 28.04.2011
comment
Я подозреваю, что вы понимаете. Блок — это просто лямбда с анонимными аргументами. Но ваш анализируется как () => () => Unit, оценивается, и его каррированный результат передается withThis(5). Вы пробовали x => JP в верхней части вашего блока? Это должно предотвратить синтаксический анализ его как () => …. Урок для меня заключается в том, чтобы быть осторожным, используя выведенные аргументы. - person Texas; 29.04.2011