当anonfun $ 1在Scala中成为anonfun $ m1 $ 1(反之亦然)?

时间:2013-11-30 14:25:24

标签: scala scalac

有人可以解释为什么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)?

2 个答案:

答案 0 :(得分:1)

似乎编译器在创建匿名类时将路径添加到名称中。你的例子(顺便说一下其他意义上的非常有趣)可以简化为:

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

幸运的是,Seq中的m1相当于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的痕迹!所以,它有点复杂,我试过但是在编译器源代码中找不到确切的选择和命名模式,尽管阅读很有趣。

答案 1 :(得分:1)

您可以观察符号操作:

$ 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实际上是special-cases anonymous functions that are owned by methods,以确定您指出的名称错误。

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

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

但是,无论如何newName获得一个新名称,我猜测保留方法名称是一种调试辅助工具。

这是一个很好的调试助手吗?

编译单元中任何方法'm'的多个anonfuns都将被命名为anonfun$m$1,依此类推;除了检查这些类别之外,无法区分anonfun$m$3是属于Foo.m还是Bar.m

我会依赖REPL来为我发现anonfuns,除了目前它并不比我们更聪明。