【问题标题】:"Parameter type in structural refinement may not refer to an abstract type defined outside that refinement"“结构细化中的参数类型可能不引用在该细化之外定义的抽象类型”
【发布时间】:2011-10-20 03:15:43
【问题描述】:

当我编译时:

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: "Parameter type in structural refinement may not refer to an abstract type defined outside that refinement" 并没有真正回答这个问题。)

【问题讨论】:

    标签: scala


    【解决方案1】:

    规范不允许。请参阅3.2.7 复合类型

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

    Bug 1906 被修复之前,编译器会编译它并且你会得到一个在运行时找不到的方法。这已在 revision 19442 中修复,这就是您收到这条精彩消息的原因。

    那么问题来了,为什么不允许这样做?

    这是 2007 年来自 scala 邮件列表的 Gilles Dubochet 的非常 detailed explanation。它大致归结为结构类型使用反射并且编译器不知道如何查找要调用的方法(如果它使用)在细化之外定义的类型(编译器不知道如何在p.getClass.getMethod("pimp", Array(?))

    中填充getMethod 的第二个参数

    但是去看看帖子,它会回答你的问题等等。

    编辑:

    你好名单。

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

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

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

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

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

    为了能够编译为 JVM 字节码,Scala 编译器更改了 将程序的 Scala 类型“擦除”(参见第 3.6 节) 参考)。擦除的类型可以在 VM 的类型系统中表示,并且 在程序上定义一个与 使用 Scala 类型键入的程序(节省了一些强制转换),虽然更少 精确的。附带说明一下,类型在 VM 中被擦除的事实 解释了为什么对类型的动态表示进行操作(模式 类型匹配)对于 Scala 的类型非常有限 系统。

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

    相反,实现了对结构定义成员的任何引用 作为一个反射调用,完全绕过虚拟机的类型系统。为了 示例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))
    

    现在是答案。

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

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

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

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

    1. “getMethod”的第二个参数是一个数组,其类型为 要查找的(结构定义的)方法的参数 - for 选择名称重载时获取的方法。这是 在处理过程中需要精确的静态类型的地方 翻译结构成员调用。通常,可利用的静态类型 为方法的参数提供结构类型 定义。在上面的例子中,“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, 数组(新 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)”。

    允许在“f”的主体内定义一个唯一的“getMethod”调用 “T”的任何实例化都需要传递给“f”的任何对象作为 “p”参数将“t”的类型擦除为“Any”。这将是一个 类成员的类型取决于如何进行的转换 程序中使用了此类的实例。这是一些东西 我们绝对不想做(也不能分开做 编译)。

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

    但是现在,对结构方法的参数使用抽象类型 types 是完全被禁止的。

    真诚地, 吉尔斯。

    【讨论】:

    • 有什么办法可以让它工作吗?如果编译器真的很努力,它可以找到类型。
    【解决方案2】:

    发布后不久发现问题:我必须定义一个命名类而不是使用匿名类。 (但仍然希望听到更好的推理解释。)

    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)
    }                                                                                                                                                                                                              
    

    有效。

    【讨论】:

    • 感谢您发布此内容,因为它也解决了我的问题。从解决方案中,我终于可以对错误所指的内容进行逆向工程;上面接受的答案对我没有帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多