6

Given a class definition with bound type parameter Animal[A <: String] it seems that the Scala compiler does not infer B <: String from Animal[B]. Is the inference allowed? How to help the compiler to do the inference?

Below is a concrete example with case classes where the lack of this inference is a problem.

Consider the following case class hierarchy:

sealed trait Person[+T <: Person[T]]
case class Student() extends Person[Student]
case class Professor() extends Person[Professor]

I need to define a case class University which I can instantiate with a variable of type Person[_], for example val p: Person[_] = Student(). I thought this would work with the following definition:

case class University(p: Person[_])

But this fails compiling with the error:

type arguments [Any] do not conform to trait Person's type parameter bounds [+T <: Person[T]]

If I bind the type parameter of the case class University it compiles (it also compiles with unbounded parameters if i drop the case keyword but this is not an option in my case):

case class BoundUniversity[P <: Person[P]](p: Person[P])

But this parametrized version cannot be instantiated with an unbounded variable of type Person[_]:

val p: Person[_] = Student()
BoundUniversity(p)

fails compiling with:

inferred type arguments [_$1] do not conform to method apply's type parameter bounds [P <: Person[P]]

The same error happens for a method with a bound argument like:

def general[P <: Person[P]](p: P) = println(p)

so this is not specific to class constructors.

Two questions:

  1. The type Person is defined with parameter bounds Person[+T <: Person[T]], so that each instance of this type is insured to respect those bounds: val p: Person[P] implies that P <: Person[P]; or am I missing something? So how can I make this clear to the compiler so that it doesn't complain?

  2. How/Can I define a case class with members with unbound type parameter like case class University(p: Person[_])?

Julien Gaugaz
  • 321
  • 3
  • 10
  • Does `T` need to be covariant? – leedm777 Apr 05 '12 at 13:44
  • @dave in my particular case `T` needs to be covariant, but I think this does not change the problem: see the introductory example. – Julien Gaugaz Apr 05 '12 at 14:15
  • You could get somewhere using [abstract types](http://docs.scala-lang.org/tutorials/tour/abstract-types.html), but then they become [pretty much invariant](http://stackoverflow.com/a/5359015/115478). – leedm777 Apr 05 '12 at 14:57

1 Answers1

2

A type X[_] is hardly ever what you want. When you use _ on a type, you are basically saying you don't care what that parameter is, because you'll never need to use it.

Anyway, this compiles. It may well bite you down the road, existential types being the tricky stuff that they are, but...

case class University(p: Person[t] forSome { type t <: Person[t] })
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • Nice existential type, thanks! This compiles, but cannot be instantiated with `val p: Person[_] = Student(); University(p)`. The type `Person[_]` I mentioned comes from a list: `List[Person[_]](Student(), Professor())`. Note that `List(Student(), Professor())` does not compile. I feel there is a pattern here but I cannot put the finger on it... – Julien Gaugaz Apr 05 '12 at 14:02
  • Actually this does not show errors in my Eclipse but does not compile with `scalac`. Type mismtach, found `Person[(some other)_1(in method equals)] where type (some other)_1(in method equals) <: schemdesc.hierarchy.eval.Person[_0]`, required `Person[_ <: schemdesc.hierarchy.eval.Person[_0]]` – Julien Gaugaz Apr 05 '12 at 14:11
  • @jullybobble The above line compiles -- I tested it. If you have a compilation problem, you are doing something different. And you cannot use `Person[_]` -- it won't work. You must use the existential type above if you want it to work. – Daniel C. Sobral Apr 05 '12 at 21:38
  • I believe you test your code before posting it. I just re-tested it from scratch, I didn't manage to make it compile on my machine with scalac 2.9.1.final (with your `University` and my `Person` definitions). I'll try to dig in that further. Anyway, thanks for the existential types hint! – Julien Gaugaz Apr 06 '12 at 06:51