无法将参数化的类型与后模式匹配的具体类型相匹配

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

使用Scala的2.12.8本没有投不会编译:

trait Content
case object A extends Content
case class B(i: Int) extends Content

def asList[C <: Content](content: C): List[C] = content match {
  case A => List(A) // compiles
  case b: B => List(b) // does not compile
}
type mismatch;
 found   : b.type (with underlying type Playground.this.B)
 required: C

这里有一个Scastie链接的问题:https://scastie.scala-lang.org/JIziYOYNTwKoZpdCIPCvdQ

为什么工作的情况下对象,而不是为案例类?我怎样才能使它的案例类工作?

EDIT

第一个答案,让我意识到我过于简化我的问题,这里是一个更新的版本:

sealed trait Content
case object A extends Content
final case class B(i: Int) extends Content

sealed trait Container[+C <: Content]
case class ContainerA(content: A.type) extends Container[A.type]
case class ContainerB(content: B) extends Container[B]

object Container {
  def apply[C <: Content](content: C): Container[C] = content match {
    case A => ContainerA(A) // compiles
    case b: B => ContainerB(b) // does not compile
  }
}

Scastie链接:https://scastie.scala-lang.org/TDlJM5SYSwGl2gmQPvKEXQ

C不能是B的子类型,因为B是最终的。

scala pattern-matching type-parameter algebraic-data-types
3个回答
1
投票

C不能是B的子类型,因为B是最终的。

错误!

Singleton types实例BB的亚型:

val b = B(0)
val container: Container[b.type] = Container[b.type](b)

由于ContainerB不延长Container[b.type],它不能被通过的最后一行返回。它不能被改变,以便它;

case class ContainerB(content: B) extends Container[content.type]

是不是在斯卡拉法律。

Null也是B的子类型,你可以创建一个类似的例子。所以是细化类型,如B { type T = Int }

这可能是无关紧要的其他亚型,因为他们没有实例:Nothing,复合类型,如B with Iterable[Int] ...


3
投票

该解决方案由@lasf评论给出:

def asList[C <: Content](content: C): List[C] = content match {
  case A => List(A) // compiles
  case b: B => List(content) // compiles
}

问题是,返回类型是List[C]但是编译器不能保证List(b)的类型是List[C]。特别是,C可能是B的亚型在这种情况下List(b)List[B]这是不符合List[C]兼容。


更新后的版本可以使用asInstanceOf得到解决,虽然这是不漂亮。

def apply[C <: Content](content: C): Container[C] = content match {
  case A => ContainerA(A) // compiles
  case b: B => ContainerB(b).asInstanceOf[Container[C]]
}

或者,你可以采取不同的方法,并使用隐式转换:

object Container {
  implicit def contain(content: A.type): Container[A.type] = ContainerA(content)
  implicit def contain(content: B): Container[B] = ContainerB(content)
}

val ca: Container[A.type] = A
val cb: Container[B] = B(0)

甚至多个构造函数:

object Container {
  def apply(content: A.type): Container[A.type] = ContainerA(content)
  def apply(content: B): Container[B] = ContainerB(content)
}

下面是一个使用typeclass的替代设计。这取代了Content类型类的Containable超。该Container类现在可以,只要有Containable该类的实例包含任何数据。

case object A
case class B(i: Int)

sealed trait Container[C]
case class ContainerA(content: A.type) extends Container[A.type]
case class ContainerB(content: B) extends Container[B]

trait Containable[T] {
  def apply(value: T): Container[T]
}
object Containable {
  implicit object AContainer extends Containable[A.type] {
    def apply(value: A.type) = ContainerA(value)
  }
  implicit object BContainer extends Containable[B] {
    def apply(value: B) = ContainerB(value)
  }
}

object Container {
  def apply[C](content: C)(implicit containable: Containable[C]): Container[C] =
    containable(content)
}

1
投票

为什么你得到一个错误的原因是因为该方法的返回类型是不明确的。上更换从列表返回类型[C]为列表[内容]解决了这个问题。

def asList[C <: Content](content: C): List[Content] = content match {
  case A => List(A) // compiles
  case b: B => List(b) // compiles
}
© www.soinside.com 2019 - 2024. All rights reserved.