0

谁能解释为什么 Scala 在以下情况下给出了两个不同的名称?为什么 Scala 不能在每种情况下都给出相同的名称?!有某种我还不知道的一致性吗?它必须与eta-expansion有关,对吧?

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)?

4

2 回答 2

2

您可以观察符号操作:

$ 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实际上是由 methods 拥有的特殊情况的匿名函数,具有您指出的名称修饰。

这是避免命名冲突的简单方法,例如:

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.mBar.m,除非通过检查这些类。

我会依靠 REPL 为我发现 anonfus,但目前它并不比我们聪明。

于 2013-12-03T08:19:03.507 回答
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)

没有序列:

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

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

幸运的是m1,在 Seq 中等价于m1 _, 并且m2等价于方法应用。名称怎么样 - 编译器添加路径到自动生成的类,因此,顶级范围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的痕迹!所以,这有点复杂,我尝试了但无法在编译器源代码中找到确切的选择和命名模式,尽管阅读很有趣。

于 2013-11-30T16:20:53.397 回答