2016-05-26 8 views
2

Warum ist nicht effects ein Effect[A]? Die Typen sollten in einer Reihe von foo stehen.Mustererkennung verliert Typgrenzen?

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

meinst du nicht: val e: Effekt [B] = effects'? –

Antwort

1

Ich habe eine Antwort nicht pro sagen, aber hier sind einige Gedanken. Zunächst einmal habe ich vereinfacht und präzisiert Code Beispiel, um das Problem zu minimieren unten:

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

Lasst uns die Fehlermeldung beachten wir für diese Zeile erhalten:

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

Es ist irgendwie seltsam die Der Compiler entscheidet, dass den Typ Effect[Any] hat. Aber mal sehen, was passiert, wenn wir diese Definition ersetzen:

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

mit diesem:

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

Jetzt bekommen wir eine andere Fehlermeldung:

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. 

In diesem Fall sind die Typen wirklich stimmt nicht überein richtig. Lass uns hindurchgehen. Wir wissen von außerhalb der case Aussage, dass wir eine ActionResult[M, A] haben. Aber wegen der Kovarianz auf dem Typ param A, das ActionResult[M, A] kann auch ein ActionResult[M, B] forSome { type B <: A } sein. Mit anderen Worten, es kann einen Typ B geben, der ein Untertyp von A ist, und foo[M, A] könnte einen ActionResult[M, B] zurückgeben. In diesem Fall wäre ein Effect[B], und da der Typparameter für Effect invariant ist, ist dieser Typ nicht kompatibel mit Effect[A].