模式匹配失去类型边界?

时间:2016-05-26 21:09:02

标签: scala

为什么effects不是Effect[A]?类型应排在foo

之后

http://scastie.org/20029

/***
scalaVersion := "2.11.8"

*/
trait Effect[A]

sealed trait ActionResult[+M, +A] {
}

sealed trait ModelUpdated[+M] extends ActionResult[M, Nothing] {
}

sealed trait HasEffect[+M, +A] extends ActionResult[M, A] {
}

sealed trait UpdateSilent

object ActionResult {

  case object NoChange extends ActionResult[Nothing, Nothing]

  final case class ModelUpdate[M](newModel: M) extends ModelUpdated[M]

  final case class ModelUpdateSilent[M](newModel: M) extends ModelUpdated[M] with UpdateSilent

  final case class EffectOnly[A](effect: Effect[A]) extends ActionResult[Nothing, A] with HasEffect[Nothing, A]

  final case class ModelUpdateEffect[M, A](newModel: M, effect: Effect[A]) extends ModelUpdated[M] with HasEffect[M, A]

  final case class ModelUpdateSilentEffect[M, A](newModel: M, effect: Effect[A])
    extends ModelUpdated[M] with HasEffect[M, A] with UpdateSilent

  def apply[M, A](model: Option[M], effect: Option[Effect[A]]): ActionResult[M, A] = (model, effect) match {
    case (Some(m), Some(e)) => ModelUpdateEffect(m, e)
    case (Some(m), None)    => ModelUpdate(m)
    case (None, Some(e))    => EffectOnly(e)
    case _                  => NoChange
  }
}

object Main {
  def foo[A, B]: ActionResult[A, B] = ???
  def dispatch[A, B] = {
    foo[A, B] match {
      case ActionResult.NoChange =>
        false
      case ActionResult.ModelUpdate(newModel) =>
        false
      case ActionResult.ModelUpdateSilent(newModel) =>
        true
      case ActionResult.EffectOnly(effects) =>
        true
      case ActionResult.ModelUpdateEffect(newModel, effects) =>
        val e: Effect[A] = effects
        false
      case ActionResult.ModelUpdateSilentEffect(newModel, effects: Effect[A]) =>
        true
    }

  }
}

1 个答案:

答案 0 :(得分:1)

我不是每个人都有答案,但这里有一些想法。首先,我已经简化并澄清了您的代码示例,以尽量减少问题:

trait Effect[A]
trait ActionResult[+M, +A]
trait ModelUpdated[+M] extends ActionResult[M, Nothing]
trait HasEffect[+M, +A] extends ActionResult[M, A]

case class ModelUpdateEffect[M, A](newModel: M, effect: Effect[A]) extends ModelUpdated[M] with HasEffect[M, A]

object Main {
  def foo[M, A]: ActionResult[M, A] = ???
  def dispatch[M, A] = {
    foo[M, A] match {
      case ModelUpdateEffect(newModel, effect) =>
        val e: Effect[A] = effect // does not compile                                                              
        false
      case _ => true
    }
  }
}

让我们注意我们为该行获取的错误消息:

type mismatch;
 found   : Effect[Any]
 required: Effect[A]
Note: Any >: A, but trait Effect is invariant in type A.

编译器决定effect的类型为Effect[Any],这有点奇怪。但是,让我们看看如果我们取代这个定义会发生什么:

case class ModelUpdateEffect[M, A](newModel: M, effect: Effect[A]) extends ModelUpdated[M] with HasEffect[M, A]

用这个:

case class ModelUpdateEffect[M, A](newModel: M, effect: Effect[A]) extends HasEffect[M, A]

现在我们收到一条不同的错误消息:

type mismatch;
 found   : Effect[?A1] where type ?A1 <: A (this is a GADT skolem)
 required: Effect[A]
Note: ?A1 <: A, but trait Effect is invariant in type A.

在这种情况下,类型真的不匹配。让我们来看看吧。我们从case声明外部知道我们有ActionResult[M, A]。但由于类型参数A的协方差,ActionResult[M, A]可能是ActionResult[M, B] forSome { type B <: A }。换句话说,某些类型B可能是A的子类型,而foo[M, A]可能会返回ActionResult[M, B]。在这种情况下,effect将是Effect[B],并且由于Effect的类型参数是不变的,因此此类型与Effect[A]不兼容。