Nicolas Rinaudo
Nicolas Rinaudo

Reputation: 6168

Apparent loss of type information when mapping over a list

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

Answers (1)

Michael Zajac
Michael Zajac

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

Related Questions