当我编译:
object Test extends App {
implicit def pimp[V](xs: Seq[V]) = new {
def dummy(x: V) = x
}
}
Run Code Online (Sandbox Code Playgroud)
我明白了:
$ fsc -d aoeu go.scala
go.scala:3: error: Parameter type in structural refinement may not refer to an abstract type defined outside that refinement
def dummy(x: V) = x
^
one error found
Run Code Online (Sandbox Code Playgroud)
为什么?
(Scala:"结构细化中的参数类型可能不会引用在该细化之外定义的抽象类型"并不真正回答这个问题.)
huy*_*hjl 24
规范不允许这样做.请参见3.2.7复合类型.
在结构改进中的方法声明中,任何值参数的类型只能引用包含在改进内的类型参数或抽象类型.也就是说,它必须引用方法本身的类型参数,或者引用改进中的类型定义.此限制不适用于函数的结果类型.
在修复Bug 1906之前,编译器已经编译了这个并且你已经得到了一个在运行时找不到的方法.这在修订版19442中得到修复,这就是为什么你得到这个美妙的信息.
问题是,为什么不允许这样做?
这是来自2007年Scala邮件列表中Gilles Dubochet的非常详细的解释.它大致归结为结构类型使用反射的事实,如果编译器使用外部定义的类型,编译器不知道如何查找调用方法细化(编译器不事先知道如何填写的第二个参数getMethod在p.getClass.getMethod("pimp", Array(?))
但是看看帖子,它会回答你的问题,还有更多.
编辑:
你好清单.
我尝试在函数参数中使用抽象数据类型定义结构类型.... 任何原因?
我最近听说过有关Scala 2.6结构类型扩展的两个问题,我想在这里回答它们.
在我回答这两个问题之前,我需要谈谈结构类型的实现.
JVM的类型系统非常基础(并且对应于Java 1.4).这意味着可以在Scala中表示的许多类型无法在VM中表示.路径依赖类型("xyA"),单例类型("a.type"),复合类型("A with B")或抽象类型都是无法在JVM类型系统中表示的类型.
为了能够编译为JVM字节码,Scala编译器将程序的Scala类型更改为"擦除"(参见参考资料的第3.6节).删除的类型可以在VM的类型系统中表示,并在程序上定义类型规则,该规则与使用Scala类型(保存一些转换)的程序的类型规则相同,但不太精确.作为旁注,在VM中擦除类型的事实解释了为什么关于类型的动态表示(类型上的模式匹配)的操作对于Scala的类型系统是非常有限的.
到目前为止,Scala中的所有类型构造都可以以某种方式擦除.结构类型不是这样.简单的结构类型"{DEF X:诠释}"不能被擦除至"对象"为VM将不允许访问的"x"字段.使用接口"interface X {int x {}; "因为擦除类型不起作用,因为任何由此类值绑定的实例都必须实现该接口,这在单独编译时无法完成.事实上,(多多包涵),其中包含比在结构类型定义的成员同名成员的任何类的任何地方必须执行相应的接口.不幸的是,甚至在已知结构类型存在之前,可以定义该类.
相反,对结构定义成员的任何引用都实现为反射调用,完全绕过VM的类型系统.例如,def f(p: { def x(q: Int): Int }) = p.x(4)将被重写为:
def f(p: Object) = p.getClass.getMethod("x", Array(Int)).invoke(p, Array(4))
Run Code Online (Sandbox Code Playgroud)
现在的答案.
Scala程序中的整数值已经经常被装箱(允许"任何"类型),从Scala自己的装箱方案中取消它们将它们立即重新装箱为java.lang.Integer将是浪费.
最糟糕的是,当反射调用具有"Any"返回类型时,返回java.lang.Integer时应该怎么做?被调用的方法可能返回一个"int"(在这种情况下它应该是unboxed并重新装箱为Scala框),或者它可能返回一个应保持不变的java.lang.Integer.
相反,我们决定将Scala自己的拳击计划改为Java.之前的两个问题就会消失.我们使用Scala的拳击方案进行的一些与性能相关的优化(预先计算最常见数字的盒装形式)也很容易与Java拳击一起使用.最后,使用Java拳击甚至比我们自己的方案快一点.
定义为抽象类型的参数类型,其中抽象类型在结构细化的范围内定义也没有问题:def f(p:{def x [T](t:T):Int})= p.xInt例如,我们知道任何传递给"f"为"p"的实例都将定义"x [T](t:T)",它必须被删除为"x(t:Object)".然后在擦除类型上正确完成查找:def f(p:Object)= p.getClass.getMethod("x",Array(Object)).invoke(p,Array(new java.lang.Integer(4) ))
但是如果结构细化范围之外的抽象类型用于定义结构方法的参数,则一切都会中断:def f [T](p:{def x(t:T):Int},t:T)= px(t)当调用"f"时,"T"可以实例化为任何类型,例如:f [Int]({def x(t:Int)= t},4)f [Any]({def x(t:Any)= 5},4)第一种情况的查找必须是"getMethod("x",Array(int))",对于第二种情况,"getMethod("x",数组(对象)) )",并且无法知道在"f"的主体中生成哪一个:"px(t)".
为了允许在"f"体内定义一个唯一的"getMethod"调用以进行"T"的任何实例化,需要将任何传递给"f"的对象作为"p"参数,将"t"类型删除为"任意" ".这将是一个转换,其中类成员的类型取决于程序中如何使用此类的实例.这是我们绝对不想做的事情(并且无法通过单独的编译完成).
或者,如果Scala支持运行时类型,则可以使用它们来解决此问题.或许有一天 ...
但是现在,仅使用结构方法的参数类型的抽象类型是被禁止的.
真诚的,吉尔斯.
Yan*_*ang 12
发布后不久发现了这个问题:我必须定义一个命名类而不是使用匿名类.(尽管如此,仍然希望听到对推理的更好解释.)
object Test extends App {
case class G[V](xs: Seq[V]) {
def dummy(x: V) = x
}
implicit def pimp[V](xs: Seq[V]) = G(xs)
}
Run Code Online (Sandbox Code Playgroud)
作品.