简体   繁体   中英

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
}

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.

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
    }
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM