当模式与类的泛型参数匹配时,模式类型不兼容

问题描述 投票:0回答:1

为什么类的泛型构造函数参数的模式匹配失败,而方法的泛型参数的模式匹配成功?

我有一个类型类

Ev
:

sealed trait Ev[T]
case object EvInt extends Ev[Int]
case object EvStr extends Ev[String]

和一个像这样的通用类:

class C[T](ev: Ev[T]) {
  val id: Int = ev match {
    case EvInt => 0
    case EvStr => 1
  }
}

以上

ev
上的模式匹配失败,并显示:

error: pattern type is incompatible with expected type;
 found   : EvInt.type
 required: Ev[T]
    case EvInt => 0

而这有效:

object C {
  def id[T](ev: Ev[T]): Int = {
    ev match {
      case EvInt => 0
      case EvStr => 1
    }
  }
}

出于我的目的,我可以通过多种方式解决这个问题,例如,将

id
移至
Ev
特征中:

sealed trait Ev[T] {
  def id: Int
}

class C[T](ev: Ev[T]) { val id: Int = ev.id }

但我很好奇为什么它会失败。

scala generics
1个回答
0
投票

认为它一开始就应该起作用是有道理的。正如一些人评论的那样,scala 2 在 ADT 和类型推断/检查方面存在一些缺陷。 并且您已经知道此问题的解决方法。所以我要在这里写下我的“实验”/假设

编译器说它需要一个

Ev[T]
,但您为它提供了
EvInt
EvString
。但我一开始的想法是,它试图在幕后说它需要
T
,但你为它提供了
Int
String
。我认为为什么会发生这种情况,可能是因为类、方法和其他标识符中的类型推断规则不同。例如,如果您将代码更改为:

class C[T](ev: Ev[T]) {
  // or use ev.asInstanceOf[Ev[_]]
  val id: Int = (ev: Ev[_]) match {
    case EvInt => 0
    case EvStr => 1
  }
}

一切都会好起来的!因为你基本上是在告诉编译器这里没有具体的

T

请注意,如果您指定类型,则不会遇到此问题:

ev match {
  case _ : EvInt.type => 0
  case _ : EvString.type => 1
}

此外,如果您使用

val
,而不是 case 对象(首先扩展自己的类型),编译错误将会消失:

val eInt: Ev[Int] = EvInt
val eStr: Ev[String] = EvString

val id: Int = ev match {
  case eInt => 0
  case eStr => 1
}

所以总的来说,我认为编译器无法证明

EvInt
EvString
Ev[*T*]
之间的关系,并且这种类型推断问题在 scala 2 中很常见。我想说这是一个错误,但我对此没有信心。

希望这有帮助!

© www.soinside.com 2019 - 2024. All rights reserved.