Can someone explain to me why this does not compile?

sealed abstract class Animal
  case class Dog(name: String) extends Animal
  case class Cat(age: Int) extends Animal


  def foo[A <: Animal](a: A): A = a match {
    case d: Dog => d
    case c: Cat => c
  }

  val dog: Dog = foo(new Dog("boxer"))
  val cat: Cat = foo(new Cat(1))

When i do foo(new Dog("boxer")) we would have let the compiler know that A is Dog and case d: Dog => d returns a Dog which type checks but the compiler complains

Error:(76, 20) type mismatch;
 found   : d.type (with underlying type sandbox.Main.Dog)
 required: A
    case d: Dog => d

All the compiler knows is that A <: Animal, so we don’t know that a Dog is an A. After all, A could be Cat. What prevents me from calling it like:


foo[Cat](new Dog("boxer"))

Maybe this is what you want?

sealed abstract class Animal
case class Dog(name: String) extends Animal
case class Cat(age: Int) extends Animal

def foo(a: Animal): Animal = a match {
  case d: Dog => d
  case c: Cat => c
}

val dog: Animal = foo(new Dog("boxer"))
val cat: Animal = foo(new Cat(1))

The above code compiles…

The compiler isn’t smart enough to know that the body of the function always returns the same type as what is fed in in this situation.

In this situation, you can workaround by returning a. In the less trivial cases where you can’t, it’s plausible that it’s not safe for all cases.

1 Like

You can’t call that per the signature, foo[Cat] expects a Cat as it’s argument, and new Dog("boxer") doesn’t conform to that type.

1 Like

Ah, you’re right.

You can overcome this kind of issues by using typeclasses instead.
Check this: https://tpolecat.github.io/2015/04/29/f-bounds.html

3 Likes

Good writeup thank you

I find it strange that the compiler isn’t smart enough to figure that out, but is smart enough for this:

sealed trait A[T]
case class B() extends A[Int]
case class C() extends A[String]

def foo[T](a: A[T]): T = a match {
  case b: B => 42
  case c: C => "foo"
}
1 Like