这不是一个完全令人满意的答案(至少对我而言),因为我不得不承认我无法准确说明推理在哪里以及为什么在这里失败。我对此只有一些模糊的直觉。该问题与编译器必须一次推断两个类型参数有关。至于为什么更改绑定到视图绑定的类型可以修复编译,我的理解是现在有两个参数列表,因此我们现在有两个连续的类型推断阶段,而不是一次两个推断。确实,以下内容:
case class Query[U <% Schema[T], T]( schema: U )
是相同的:
case class Query[U, T]( schema: U )( implicit conv: U => Schema[T] )
第一个参数列表驱动 的推理U
,然后第二个参数列表(注意U
现在已知)将驱动 的推理T
。
在表达式的情况下Query( People )
,参数People
将驱动类型推断器设置U
为People.type
。然后,编译器将寻找从People.type
to的隐式转换Schema[T]
,以传入第二个参数列表。范围内唯一的一个是从People.type
到的(微不足道的)转换Schema[Person]
,驱动推理器推断出T = Person
。
要在不使用视图绑定的情况下修复编译,可以将类型参数替换T
为抽象类型:
case class Person( val name: String )
sealed trait Schema {
type T
}
abstract class SchemaImpl[_T] extends Schema {
type T = _T
}
object People extends SchemaImpl[Person]
case class Query[U <: Schema]( schema: U ) {
def results: Seq[schema.T] = ???
}
class TypeText extends Application {
val query = Query( People )
}
更新:
@Aaron Novstrup 的:据我所知,您的答案不正确(更新更新:Aaron 的原始答案声称该Query
声明等同于case class Query[U <: Schema[X], T](schema: U)
)。
case class Query[U <: Schema[X], T](schema: U)
甚至不编译。假设你的意思是
case class Query[U <: Schema[_], T](schema: U)
(它确实编译),很容易在 REPL 中检查它也不相同。
确实,以下编译得很好:
case class Query[U <: Schema[_], T](schema: U)
type MyQuery = Query[Schema[String], Int]
虽然,以下没有:
case class Query[U <: Schema[T], T](schema: U)
type MyQuery = Query[Schema[String], Int]
因此证明了差异。错误是:
<console>:10: error: type arguments [Schema[String],Int] do not conform to class Query's type parameter bounds [U <: Schema[T],T]
type MyQuery = Query[Schema[String], Int]
这清楚地表明,第一次和第二次出现T
表示相同的类型,并且我们确实在两个类型参数之间存在关系。