Reputation: 6168
I'm trying to declare a case class that takes two type parameters: a concrete type A
and a type constructor D
, constrained to be a subclass of a known one. My case class would then contain a List[D[A]]
.
The problem I'm encountering is that, whenever I map on that list, I seem to lose the information of the type contained by D
.
Here's what I have:
// Represents a named collection of documents of type A
trait Documents[A] {
def name: String
def take(target: Int): Documents[A]
// [...]
}
// Represents a collection of named collections of documents.
// A is the type of a document, D that of a named collection of documents
case class Data[A, D[_] <: Documents[_]](docs: List[D[A]]) {
// Makes sure no D contains more than target documents
def cap(target: Int): Data[A, Documents] = {
Data(docs.map(_.take(target)))
}
// [...]
}
This fails to compile with the following confusing (to me) error message:
type mismatch;
[error] found : List[Documents[_]]
[error] required: List[Documents[A]]
[error] Data(docs.map(_.take(target)))
[error] ^
I don't understand how applying a function D[A] => Documents[A]
to a D[A]
can yield a Documents[_]
...
Upvotes: 1
Views: 54
Reputation: 55569
The problem is that Documents[_]
is using an existential type, which means you are telling the compiler you don't care about the type that is contained within D
. So even if you specify that docs
is a List[D[A]]
, the compiler really only treats it as List[D[_]]
. What you actually want is to use a free type parameter, and not a wild-card in the type constraint. i.e. D[B] <: Documents[B]
import scala.language.higherKinds
trait Documents[A] {
def name: String
def take(target: Int): Documents[A]
}
case class Data[A, D[B] <: Documents[B]](docs: List[D[A]]) {
def cap(target: Int): Data[A, Documents] = {
Data(docs.map(_.take(target)))
}
}
Upvotes: 1