This is not a fully statisfying answer (at least to me) as I have to admit that I cannot put words on exactly where and why the inference fails here. I only have some fuzzy intuitions about it.
The problem is related to the compiler having to infer two type parameters at a time.
As to why changing the type bound to a view bound fixes the compilation, my understanding is that now there are two parameter lists, and that as a result we now have two successive phases of type inferences instead of
two inferences at a time. Indeed, the following:
case class Query[U <% Schema[T], T]( schema: U )
is the same as:
case class Query[U, T]( schema: U )( implicit conv: U => Schema[T] )
The first parameter list drives the inference of U
, and then the second one (note that U
is now know) will drive the inference of T
.
In the case of the expression Query( People )
, the parameter People
will drive the type inferencer to set U
to People.type
. Then, the compiler will look for an implicit conversion from People.type
to Schema[T]
, to pass in the second parameter list. The only one in scope is the (trivial) conversion from People.type
to Schema[Person]
, driving the inferencer to deduce that T = Person
.
To fix the compilation without resorting to a view bound, you can replace the type parameter T
with an abstract type:
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 )
}
UPDATE:
@Aaron Novstrup's:
To the extent of my knowledge, your answer is incorrect (update to the update: the orignal answer from Aaron claimed that the Query
declaration was equivalenbt to case class Query[U <: Schema[X], T](schema: U)
).
case class Query[U <: Schema[X], T](schema: U)
does not even compile.
Let's say that you meant
case class Query[U <: Schema[_], T](schema: U)
(which does compile), it's easy to check in the REPL that it is not the same either.
Indeed, the following compiles fine:
case class Query[U <: Schema[_], T](schema: U)
type MyQuery = Query[Schema[String], Int]
While, the following does not:
case class Query[U <: Schema[T], T](schema: U)
type MyQuery = Query[Schema[String], Int]
Hence proving the difference. The error is:
<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]
Which clearly shows that the first and second occurences of T
denote the same type, and we do have a relationship between the two type parameters.