有人可以解释为什么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
)?
答案 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,除了目前它并不比我们更聪明。