7

Я использую _ в качестве заполнителя для создания анонимной функции, и проблема в том, что я не могу предсказать, как Scala собирается преобразовать мой код. Точнее, он ошибочно определяет, как «большая» анонимная функция я хочу.Каковы правила управления подчеркиванием для определения анонимной функции?

List(1,2,3) foreach println(_:Int) //error ! 
List(1,2,3) foreach (println(_:Int)) //work 
List(1,2,3) foreach(println(_:Int)) //work 

Использование -Xprint:typer я могу видеть, Scala трансформирует первый в «большую анонимную функцию»:

x$1 => List(1,2,3) foreach(println(x$1:Int)) 

обработана 2th 3th является правом превращения в то, что я хочу.

... foreach (x$1 => println(x$1:Int)) 

Почему это? Каково правило?

ответ

8

Простые правила, чтобы определить объем подчеркиванием:

  1. Если подчеркивание является аргументом к способу, то объем будет вне этот метод, в противном случае соответствующие приведенные ниже нормы;
  2. Если подчеркивание находится внутри выражения, ограниченного() или {}, будет использоваться самый внутренний такой разделитель, который содержит символ подчеркивания;
  3. При прочих равных условиях наибольшее возможное выражение будет использовано.

Таким образом, по правилу # 1, вместо того, чтобы println((x: Int) => x), объем будет размещен снаружи (в том числе) println.

По правилу № 2 последние два примера будут иметь функцию, ограниченную скобками, поэтому (x => println(x: Int)).

В соответствии с правилом № 3 первым примером будет все выражение, так как не существует ограничивающих круглых скобок.

4

Я верю, что ответ мистера Собрала неверен. Фактические правила можно найти в Scala Language Reference, раздел 6.23, подзаголовок «Синтаксис заполнитель для анонимных функций».

Единственное правило заключается в том, что внутреннее выражение , содержащее символ подчеркивания, определяет область действия анонимной функции. Это означает, что первые два правила г-на Собрала верны, потому что вызов метода является выражением и в скобках выражение не меняет его значения. Но третье правило противоположно истине: при прочих равных условиях будет использовано наименьшее выражение, которое имеет смысл.

К сожалению, мое объяснение поведения г-на Ласковски, наблюдаемое за его первым примером, немного связано и спекулятивно. Когда

List(1,2,3) foreach println(_:Int) 

Набирается в виде шкалы для чтения и печати Scala.Сообщение об ошибке:

error: type mismatch; 
found : Unit 
required: Int => ? 
       List(1,2,3) foreach println(_:Int) 
             ^

Если вы меняете на примере крошечную:

List(1,2,3).foreach println(_:Int) 

сообщение об ошибке легче сделать чувство -

error: missing arguments for method foreach in class List; 
follow this method with `_' if you want to treat it as a partially applied function 
      List(1,2,3).foreach println(_:Int) 
        ^

Чтобы понять вещи немного лучше, позвоните по номеру scala: scala -Xprint:parser, который после каждого выражения вводится пользователем, вызывает выражение, которое выводится с помощью синтаксического анализатора для печати. (Наряду с большим количеством мусора, который я опущу.) Для первого примера Ласковски в выражение понято парсера

((x$1: Int) => List(1, 2, 3).foreach(println((x$1: Int)))) 

Для второго примера, версия парсера является

((x$1: Int) => List(1, 2, 3).foreach.println((x$1: Int))) 

По-видимому, правило области применения применяется до того, как структура выражения полностью скомпонована. В обоих случаях синтаксический анализатор догадывается, что наименьшее выражение начинается в List, хотя после того, как вставлены парсеры, это уже не так. Во втором примере в дополнение к этому предположению предполагается, что, поскольку println является идентификатором, foreach println представляет собой цепочку методов, первая из которых не имеет аргументов. Ошибка при foreach затем попадает перед ошибкой в ​​println, маскируя ее. Ошибка в println заключается в том, что ее результатом является Unit, а для функции foreach требуется функция. Как только вы увидите дерево синтаксического анализа, легко увидеть, что это правильно, но мне непонятно, почему дерево разбора является тем, чем оно является.