When you compile greet(p)
, the compiler has to infer the type P
in
def greet[P <: Person](person: P): (P, String)
If you then defined a subclass of Person and call greet on that an instance:
class Parent(val name: String) extends Person
val parent = new Parent("Joe")
val (p, greeting) = greet(parent)
The inferred type P is determined at compile time, it does not depend on the runtime type of parent. So the compiler would have to infer P
as Parent
: greet[Parent](parent)
.
But then how would these expressions be typed? In order to type check, since P
is Parent
, they have to be of type (Parent, String)
:
case teacher @ Teacher(name, students) => teacherGreeting(teacher)
case Teacher(name, students) => teacherGreeting(person)
In the first case, the return type of teacherGreeting(teacher)
is (Teacher, String)
. And Teacher
is not a Parent
. The return type does not match.
In the second case, you are calling teacherGreeting(person: Parent)
so it is wrong type for the argument.
When you say you don't have a problem if you inline the body of teacherGreeting
inside the second case, that is probably because you return (person, "str")
. And in that case person
would be of type P
for sure. But when you pass it through teacherGreeting
, the compiler does not know that you are returning the passed argument of type P
. For all we know you could be returning Teacher("another", List())
.
Edit: so thinking on how to preserve the type P
here is a (cumbersome) way to go about it. You want to retain the type through the teacherGreeting
call. This can be done like this. Use a type parameter Q
that will be inferred as the P
from greet
:
def teacherGreeting[Q <: Teacher](teacher: Q): (Q, String) = {
val names = teacher.students.map(_.name).mkString(", ")
(teacher, s"Hello ${teacher.name}, your students are $names")
}
Tell the compiler that teacher
is a P
and a Teacher
:
def greet[P <: Person](person: P): (P, String) = person match {
case Student(name) => (person, s"Hello $name")
case teacher: (P with Teacher) => teacherGreeting(teacher)
}