【问题标题】:When anonfun$1 becomes anonfun$m1$1 (or vice versa) in Scala?当 anonfun$1 在 Scala 中变成 anonfun$m1$1 (反之亦然)?
【发布时间】:2013-11-30 14:25:23
【问题描述】:

谁能解释为什么 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)?

【问题讨论】:

    标签: scala scalac


    【解决方案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)
    

    没有序列:

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

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

    【讨论】:

      【解决方案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 实际上是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'中的多个匿名函数都将被命名为anonfun$m$1等等;没有办法区分 anonfun$m$3 是属于 Foo.m 还是 Bar.m,除非检查这些类。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-18
        • 2023-04-02
        • 2012-07-16
        • 1970-01-01
        • 1970-01-01
        • 2011-01-25
        相关资源
        最近更新 更多