2016-05-26 4 views
2

Почему не effects a 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 
    } 

    } 
} 
+0

Вы не имеете в виду 'val e: Effect [B] = effects'? –

ответ

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]. Но из-за ковариации по типу param 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].

Смежные вопросы