Steven Noble
Steven Noble

Reputation: 10415

Why does this stop working once I introduce a parameter to the type? (scala)

I'm working with this program

abstract class Foo[+T[_]] {}
case object Unit extends Foo
case class Cons[+T[_]](a: Foo[T], b: Foo[T]) extends Foo[T]
case class Strings[T[_]](x: T[String]) extends Foo[T]
def first[T[_]](v: Foo[T]): Option[Foo[T]] = v match {
  case Cons(a, b) => Some(a)
  case _ => None
}

and getting the error

constructor cannot be instantiated to expected type;
 found   : Cons[T]
 required: Foo[?T1] where type ?T1 <: T (this is a GADT skolem)

But if I get rid of the parameter for T it works just fine?

abstract class Foo[+T] {}
case object Unit extends Foo
case class Cons[+T](a: Foo[T], b: Foo[T]) extends Foo[T]
case class Val[T](x: T) extends Foo[T]
def first[T](v: Foo[T]): Option[Foo[T]] = v match {
  case Cons(a, b) => Some(a)
  case _ => None
}

Upvotes: 2

Views: 107

Answers (2)

Dmytro Mitin
Dmytro Mitin

Reputation: 51658

This is because of covariance of Foo and Cons. If you remove +s everything will compile. In covariant case when you match v of type Foo[T] with pattern Cons(a, b) this a is not necessarily of type Foo[T] with the same T, a can be of any type Foo[T'] with T' >: T (or Foo[Any]), which contradicts return type. With your custom unapply you remove this uncertainty in type.

Upvotes: 2

Steven Noble
Steven Noble

Reputation: 10415

I seem to be able to solve this by supplying my own Cons.unapply. I'm unclear why I need to write my own.

object Cons {
  def unapply[T[_]](v: Foo[T]): Option[(Foo[T], Foo[T])] = {
    if(v.isInstanceOf[Cons[T]]) {
      v.asInstanceOf[Cons[T]] match {
        case Cons(a,b) => Some((a,b))
        case _ => None
      }
    } else {
      None
    }
  }
}

Upvotes: 0

Related Questions