该示例的细微变化会提供更有用的错误消息:
scala> l: (A[B] forSome { type B[_] })
<console>:10: error: type mismatch;
found : A[List]
required: A[_[_] <: Any]
Note: List <: Any, but trait A is invariant in type H.
You may wish to define H as +H instead. (SLS 4.5)
l: (A[B] forSome { type B[_] })
^
<console>:10: error: can't existentially abstract over parameterized type B
l: (A[B] forSome { type B[_] })
^
查找此错误会将我们带到编译器中的 TODO。
由于存在类型将会消失,根据 Odersky 的电子邮件,我认为这个限制不会被修复。不过,Martin Odersky 的邮件也提醒我们存在类型等价于抽象类型。因此,上面的例子可以编码如下:
scala> trait A { type H[_] }
defined trait A
scala> val l: A {type H[X] = List[X]} = null
l: A{type H[X] = List[X]} = null
scala> l: A
res0: A = null
类型应用在语法上很丑陋,但是将值转换为存在变得微不足道(也可以在编译器中实现,这是 Odersky 观点的一部分)。
编码存在的有用之处在于类型成员不必被实例化。所以,编码A[_]
我们可以写:
scala> class A { type T }
defined class A
scala> new A
res1: A = A@3a7049a6
这里令人困惑的是,这不适用于对象:
scala> object B { type T }
<console>:8: error: only classes can have declared but undefined members
object B { type T }
^
但我最近将它作为一个错误接受了——请参阅此处获取澄清规范的拉取请求(由 Adriaan Moors 批准),以及此处查看我的错误报告和对编译器的单行修复(仍在等待审核)。