“结构细化中的参数类型可能不是指在该细化之外定义的抽象类型" [英] "Parameter type in structural refinement may not refer to an abstract type defined outside that refinement"

查看:30
本文介绍了“结构细化中的参数类型可能不是指在该细化之外定义的抽象类型"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我编译时:

object Test extends App {
  implicit def pimp[V](xs: Seq[V]) = new {
    def dummy(x: V) = x
  }
}                                                                                                                                                                                                              

我明白了:

$ 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

为什么?

(Scala:结构细化中的参数类型可能不会引用在该细化之外定义的抽象类型"t真的回答这个.)

推荐答案

规范不允许这样做.请参阅3.2.7 复合类型.

It's disallowed by the spec. See 3.2.7 Compound Types.

在结构细化的方法声明中,任何值参数的类型只能引用包含在细化中的类型参数或抽象类型.也就是说,它必须引用方法的类型参数本身,或细化中的类型定义.此限制不适用到函数的结果类型.

Within a method declaration in a structural refinement, the type of any value parameter may only refer to type parameters or abstract types that are contained inside the refinement. That is, it must refer either to a type parameter of the method itself, or to a type definition within the refinement. This restriction does not apply to the function’s result type.

Bug 1906 被修复之前,编译器会编译这个,你'会得到一个在运行时找不到的方法.这已在 修订版 19442 中得到修复,这就是您收到这条精彩信息的原因.

Before Bug 1906 was fixed, the compiler would have compiled this and you'd have gotten a method not found at runtime. This was fixed in revision 19442 and this is why you get this wonderful message.

问题是,为什么不允许这样做?

The question is then, why is this not allowed?

这里很详细解释 来自 2007 年 Scala 邮件列表中的 Gilles Dubochet.大致归结为结构类型使用反射,编译器不知道如何查找要调用的方法(如果使用)在细化之外定义的类型(编译器提前不知道如何填充 p.getClass.getMethod("pimp", Array(?))getMethod 的第二个参数

Here is very detailed explanation from Gilles Dubochet from the scala mailing list back in 2007. It roughly boils down to the fact that structural types use reflection and the compiler does not know how to look up the method to call if it uses a type defined outside the refinement (the compiler does not know ahead of time how to fill the second parameter of getMethod in p.getClass.getMethod("pimp", Array(?))

但是去看看帖子,它会回答你的问题以及更多.

But go look at the post, it will answer your question and some more.

你好列表.

我尝试在函数中使用抽象数据类型定义结构类型范围.... 有什么理由吗?

I try to define structural types with abstract datatype in function parameter. ... Any reason?

我听说过关于结构类型的两个问题最近 Scala 2.6 的扩展,我想在这里回答.

I have heard about two questions concerning the structural typing extension of Scala 2.6 lately, and I would like to answer them here.

  1. 为什么我们要更改 Scala 的原生值(int"等)装箱方案到 Java 的(java.lang.Integer").
  2. 为什么对结构定义的参数有限制方法(结构细化中的参数类型可能不参考抽象类型定义在相同的细化之外")需要.

在回答这两个问题之前,我需要先谈谈结构类型的实现.

Before I can answer these two questions, I need to speak about the implementation of structural types.

JVM 的类型系统非常基础(对应于 Java 1.4).那意味着可以在 Scala 中表示的许多类型不能在 VM 中表示.路径依赖类型(x.y.A"),单例类型(a.type")、复合类型(A with B")或抽象类型都是类型无法在 JVM 的类型系统中表示.

The JVM's type system is very basic (and corresponds to Java 1.4). That means that many types that can be represented in Scala cannot be represented in the VM. Path dependant types ("x.y.A"), singleton types ("a.type"), compound types ("A with B") or abstract types are all types that cannot be represented in the JVM's type system.

为了能够编译为 JVM 字节码,Scala 编译器更改了程序的 Scala 类型到它们的擦除"(参见第 3.6 节)参考).擦除的类型可以在 VM 的类型系统中表示,并且在程序上定义一个类型规则,它等同于使用 Scala 类型键入的程序(保存一些强制转换),尽管更少精确的.作为旁注,类型在 VM 中被擦除的事实解释了为什么对类型的动态表示进行操作(模式类型匹配)在 Scala 的类型方面非常受限制系统.

To be able to compile to JVM bytecode, the Scala compilers changes the Scala types of the program to their "erasure" (see section 3.6 of the reference). Erased types can be represented in the VM's type system and define a type discipline on the program that is equivalent to that of the program typed with Scala types (saving some casts), although less precise. As a side note, the fact that types are erased in the VM explains why operations on the dynamic representation of types (pattern matching on types) are very restricted with respect to Scala's type system.

到目前为止,Scala 中的所有类型结构都可以通过某种方式删除.这不适用于结构类型.简单结构类型{ defx: Int }" 不能被擦除为Object",因为 VM 不允许访问x"字段.使用接口interface X { int x{};}"因为擦除的类型也不起作用,因为任何实例都受这种类型的值必须实现那个不能的接口在单独编译的情况下完成.确实(忍受我)任何包含与定义的成员同名的成员的类任何地方的结构类型都必须实现相应的界面.不幸的是,这个类甚至可能在已知存在结构类型.

Until now all type constructs in Scala could be erased in some way. This isn't true for structural types. The simple structural type "{ def x: Int }" can't be erased to "Object" as the VM would not allow accessing the "x" field. Using an interface "interface X { int x{}; }" as the erased type won't work either because any instance bound by a value of this type would have to implement that interface which cannot be done in presence of separate compilation. Indeed (bear with me) any class that contains a member of the same name than a member defined in a structural type anywhere would have to implement the corresponding interface. Unfortunately this class may be defined even before the structural type is known to exist.

相反,任何对结构定义成员的引用都被实现作为反射调用,完全绕过 VM 的类型系统.为了示例 def f(p: { def x(q: Int): Int }) = p.x(4) 将被重写类似于:

Instead, any reference to a structurally defined member is implemented as a reflective call, completely bypassing the VM's type system. For example def f(p: { def x(q: Int): Int }) = p.x(4) will be rewritten to something like:

  def f(p: Object) = p.getClass.getMethod("x", Array(Int)).invoke(p, Array(4))

现在是答案.

  1. invoke"将使用装箱(java.lang.Integer")值,只要调用的方法使用本机值(int").这意味着上述调用必须看起来像...invoke(p, Array(newjava.lang.Integer(4)).intValue".

Scala 程序中的整数值已经经常被装箱(以允许任何"类型),并且将它们从 Scala 自己的类型中拆箱是很浪费的将它们立即重新装箱为 java.lang.Integer 的装箱方案.

Integer values in a Scala program are already often boxed (to allow the "Any" type) and it would be wasteful to unbox them from Scala's own boxing scheme to rebox them immediately as java.lang.Integer.

最糟糕的是,当反射调用具有Any"返回类型时,返回 java.lang.Integer 时应该怎么做?被称为方法可能会返回一个int"(在这种情况下它应该是拆箱并重新装箱为 Scala 盒子)或者它可能会返回一个java.lang.Integer 应该保持不变.

Worst still, when a reflective call has the "Any" return type, what should be done when a java.lang.Integer is returned? The called method may either be returning an "int" (in which case it should be unboxed and reboxed as a Scala box) or it may be returning a java.lang.Integer that should be left untouched.

相反,我们决定将 Scala 自己的装箱方案更改为 Java 的.这之前的两个问题就消失了.一些性能相关的我们对 Scala 的装箱方案进行的优化(预先计算最常见数字的盒装形式)在 Java 中易于使用拳击也是.最后,使用 Java 拳击甚至比我们自己的方案.

Instead we decided to change Scala's own boxing scheme to Java's. The two previous problems then simply disappear. Some performance-related optimisations we had with Scala's boxing scheme (pre-calculate the boxed form of the most common numbers) were easy to use with Java boxing too. In the end, using Java boxing was even a bit faster than our own scheme.

  1. getMethod"的第二个参数是一个数组,其类型为要查找的(结构定义的)方法的参数 - 用于选择名称重载时要获取的方法.这是在一个地方,在这个过程中需要精确的静态类型翻译结构成员调用.通常,可利用的静态类型为方法的参数提供了结构类型定义.在上面的例子中,x"的参数类型是已知的是Int",它允许查找.

参数类型定义为抽象类型,其中抽象类型为定义在结构细化范围内是没有问题的任何一个:def f(p: { def x[T](t: T): Int }) = p.xInt在这个例子中,我们知道任何作为p"传递给f"的实例都会定义x[T](t: T)",它必须被擦除为x(t: Object)".这然后在已擦除的类型上正确完成查找:def f(p: Object) = p.getClass.getMethod("x", Array(Object)).invoke(p,数组(new java.lang.Integer(4)))

Parameter types defined as abstract types where the abstract type is defined inside the scope of the structural refinement are no problem either: def f(p: { def x[T](t: T): Int }) = p.xInt In this example we know that any instance passed to "f" as "p" will define "x[T](t: T)" which is necessarily erased to "x(t: Object)". The lookup is then correctly done on the erased type: 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) = p.x(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", Array(Object))",以及无法知道在体内生成哪个f":p.x(t)".

But if an abstract type from outside the structural refinement's scope is used to define a parameter of a structural method, everything breaks: def f[T](p: { def x(t: T): Int }, t: T) = p.x(t) When "f" is called, "T" can be instantiated to any type, for example: f[Int]({ def x(t: Int) = t }, 4) f[Any]({ def x(t: Any) = 5 }, 4) The lookup for the first case would have to be "getMethod("x", Array(int))" and for the second "getMethod("x", Array(Object))", and there is no way to know which one to generate in the body of "f": "p.x(t)".

允许在f"的主体内定义一个唯一的getMethod"调用T"的任何实例化都需要传递给f"的任何对象作为p"参数将t"的类型擦除为Any".这将是一个类成员的类型取决于如何转换此类的实例在程序中使用.这是什么我们绝对不想做(并且不能用单独的编译).

To allow defining a unique "getMethod" call inside "f"'s body for any instantiation of "T" would require any object passed to "f" as the "p" parameter to have the type of "t" erased to "Any". This would be a transformation where the type of a class' members depend on how instances of this class are used in the program. And this is something we definitely don't want to do (and can't be done with separate compilation).

或者,如果 Scala 支持运行时类型,则可以使用它们来解决这个问题.也许有一天...

Alternatively, if Scala supported run-time types one could use them to solve this problem. Maybe one day ...

但是现在,使用抽象类型作为结构方法的参数类型是完全禁止的.

But for now, using abstract types for structural method's parameter types is simply forbidden.

真诚的,吉尔斯.

这篇关于“结构细化中的参数类型可能不是指在该细化之外定义的抽象类型"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆