Domanda

Can anyone explain why Scala gives two different names in the following cases? Why couldn't Scala give the same names in each case?! Is there some kind of consistency I don't know yet? It must be related to eta-expansion, right?

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

The above gives List(anonfun$1) - note the name of the element - anonfun$1 while the following gives anonfun$m1$1. Why?

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

Could I also ask for a simpler case to demonstrate the difference (perhaps without using Seq)?

È stato utile?

Soluzione

It seems compiler adds path into name when creates anonymous classes. Your example (pretty interesting in some other sense by the way) may be simplified down to:

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

Which produces:

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

Without Seq:

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

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

Luckily m1 in Seq is equivalent to m1 _, and m2 is equivalent to method application. What about names - compiler adds path to autogenerated classes, so, top-scope m1 turns to anonfun$1 (anonfun is default prefix for generated classes for functions), and because m2 returns function from inside, that function gets one more element in path (name).

Interestingly, this weird thing:

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

Has name:

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

So, no trace of a, b, c! So, it's somewhat complicated, I tried but couldn't have found the exact choice and patterns of naming in compiler source, though reading was interesting.

Altri suggerimenti

You can observe symbol manipulation:

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

In the REPL, be forewarned, you'll see one ream of output for your wrapped code and then a second ream of output for the bit of code that prints your result.

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

...massive snip...

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


[[symbol layout at end of lambdalift]]

...snip...

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

As the output says, the anonfun becomes a child of the enclosing class because it is implemented as a class; any captured variables are passed to its constructor.

A quick look at LambdaLift.scala shows that newName actually special-cases anonymous functions that are owned by methods, to have the name-mangling you've pointed out.

This is an easy way to avoid naming collisions such as:

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

But since newName is getting a fresh name anyway, I would guess that retaining the method name is a debugging aid.

Is it a good debugging aid?

Multiple anonfuns in any method 'm' in a compilation unit will all be named anonfun$m$1 and so on; there is no way to distinguish whether the anonfun$m$3 belonged to Foo.m or Bar.m, except by inspecting those classes.

I would rely on the REPL to discover anonfuns for me, except currently it is no smarter than we are.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top