Подчеркивание и конкатенация строк в List.map с Scala

Scala позволяет использовать подчеркивание для создания простой карты. Так, например, вместо того, чтобы писать:

def roleCall(people: String*){
  people.toList.map(x => println(x))
}  

... вместо этого я могу написать:

def roleCall(people: String*){
  people.toList.map(println(_))
}  

Однако по какой-то причине я не могу написать:

def greet(people: String*){
  // This won't compile!
  people.toList.map(println("Hello " + _))
}

вместо этого я должен написать:

def greet(people: String*){
  people.toList.map(x => println("Hello " + x))
}

Кто-нибудь может объяснить почему?

1 ответ

Решение

По сути, сокращенный синтаксис для определений функций работает, как и ожидалось, только если нет вложенных скобок. Это потому, что каждый уровень вложенности создает свою собственную область, в которой живут подчеркивания. В REPL вы можете проверить, например, что:

scala> val list = List(1,2,3).map(_ + 1)
list: List[Int] = List(2, 3, 4)

а также

scala> val list = List(1,2,3).map((_ + 1))
list: List[Int] = List(2, 3, 4)

оба работают, но как только вы добавите что-нибудь после скобок, вы получите ошибку:

val list = List(1,2,3).map((_ + 1) + 1)
<console>:58: error: missing parameter type for expanded function ((x$1) => x$1.$plus(1))
       val list = List(1,2,3).map((_ + 1) + 1)

Как видите, ошибка относится только к функции (_ + 1) (показана как ((x$1) => x$1.$plus(1)) в сообщении), а не ко всему выражению (_ + 1) + 1 поэтому часть в скобках рассматривается как отдельная функция и подчеркивание _ интерпретируется в рамках этой внутренней функции, а не внешней.

Я не совсем уверен, почему List(1,2,3).map(println(_)) работает, но, похоже, это некий крайний случай, который из-за единственного аргумента, кажется, просто работает по простому совпадению. Я был бы рад сам узнать подробности. В любом случае, использование скобок внутри определений анонимных функций с подчеркиванием неизбежно вызовет проблемы рано или поздно, и их лучше избегать.

Другие вопросы по тегам