Когда anonfun$1 становится anonfun$m1$1 (или наоборот) в Scala?

Кто-нибудь может объяснить, почему Scala дает два разных имени в следующих случаях? Почему Scala не может дать одинаковые имена в каждом случае?! Есть ли какая-то последовательность, которую я еще не знаю? Это должно быть связано с eta-расширением, верно?

object A {
  val ms: Seq[String => String] = Seq(m1)
  def m1(s: String) = s
}
A.ms.map(m => m.getClass.getSimpleName)

Выше дает List(anonfun$1) - запомните название элемента - anonfun$1 в то время как следующее дает anonfun$m1$1, Зачем?

object A {
  val ms: Seq[String => String] = Seq(m1)
  def m1: String => String = s => s
}
A.ms.map(m => m.getClass.getSimpleName)

Могу ли я также попросить более простой случай, чтобы продемонстрировать разницу (возможно, без использования Seq)?

2 ответа

Решение

Кажется, компилятор добавляет путь к имени, когда создает анонимные классы. Ваш пример (кстати, довольно интересный в каком-то другом смысле) может быть упрощен до:

def m1(s: String) = s
def m2: String => String = s => s
val ss: Seq[String => String] = Seq(m1, m2)
ss map (_.getClass.getSimpleName)

Который производит:

res28: Seq[String] = List(anonfun$1, anonfun$m2$1)

Без Seq:

(m1 _).getClass.getSimpleName
res34: String = anonfun$1

m2.getClass.getSimpleName
res35: String = anonfun$m2$1

к счастью m1 в Seq эквивалентно m1 _, а также m2 эквивалентно применению метода. Что касается имен - компилятор добавляет путь к автоматически сгенерированным классам, поэтому top-scope m1 превращается в anonfun$1 (anonfun является префиксом по умолчанию для сгенерированных классов для функций), и потому m2 возвращает функцию изнутри, эта функция получает еще один элемент в пути (имя).

Интересно, что это странная вещь:

def a() = {
  def b() = {
    def c() = {
      def d(): String => String = s => s
      d()
    }
    c()
  }
  b()
}

Имеет имя:

a().getClass.getSimpleName
res30: String = anonfun$d$1$1

Итак, никаких следов a, b, c! Итак, это несколько сложно, я пытался, но не смог найти точный выбор и шаблоны именования в исходном коде компилятора, хотя чтение было интересным.

Вы можете наблюдать манипуляции с символами:

$ scala -Yshow-syms -uniqid -Dscala.repl.maxprintstring=8000

В REPL, будьте предупреждены, вы увидите один пакет вывода для вашего упакованного кода, а затем второй пакет вывода для фрагмента кода, который печатает ваш результат.

scala> def m1: String => String = s => s

[[symbol layout at end of parser]]
* package scala#22 (final)

[[symbol layout at end of namer]]
* object $read#58183
* package $line6#58181 (final)
  package scala#22 (final)

[[symbol layout at end of packageobjects]]
  object $read#58183
  package $line6#58181 (final)
  package scala#22 (final)

[[symbol layout at end of typer]]
* class String#643 (final)
* constructor Object#3505
* object $read#58184
*     constructor $read#58188
*     object $iw#58190
*         constructor $iw#58192
*         object $iw#58193
*         object $iw#58194
*             constructor $iw#58196
*             method m1#58197
*                 value $anonfun#58199 (<synthetic>)
*                     value s#58200

... массивный снайпер...

          object $iw#58194
              constructor $iw#58196
              method m1#58197
                  <$anon: Function1#2093> (final <synthetic>)
                      constructor $anonfun#58218


[[symbol layout at end of lambdalift]]

... чик...

          object $iw#58194
O             <$anon: Function1#2093> [Owner was method m1#58197, now object $iw#58194] (final <synthetic>)
                  constructor $anonfun$m1$1#58218

Как говорится в выходных данных, anonfun становится дочерним для включающего класса, потому что он реализован как класс; любые захваченные переменные передаются его конструктору.

Быстрый взгляд на LambdaLift.scala показывает, что newName на самом деле, это особые случаи анонимных функций, которые принадлежат методам, чтобы указывать имя, на которое вы указали.

Это простой способ избежать конфликтов имен, таких как:

scala> class Foo { def x = 1 to 10 map (2 * _) ; def y = 1 to 10 map (3 * _) filter (_ > 6) }

Но с тех пор newName в любом случае, получаю новое имя, я думаю, что сохранение имени метода - это помощь при отладке.

Это хорошая помощь отладки?

Несколько анонфунов в любом методе m в модуле компиляции будут названы anonfun$m$1 и так далее; нет никакого способа отличить anonfun$m$3 принадлежал Foo.m или же Bar.mкроме проверки этих классов.

Я бы положился на REPL, чтобы найти для меня анонфун, за исключением того, что в настоящее время он не умнее нас.

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