Scala Case matching matching erreur de compilation avec des types internes aliasés?

StackOverflow https://stackoverflow.com/questions/1812695

  •  06-07-2019
  •  | 
  •  

Question

Comment utiliser la correspondance de classe de cas avec des types avec alias? Cela fonctionne lorsque je retire CB, etc. du conteneur.

class DoStuff[TKey](
  val c : Container[TKey]#CB
)
{
  type CB = Container[TKey]#CB
  type C1 = Container[TKey]#C1
  type C2 = Container[TKey]#C2

  c match {
    case C1(e1) => e1  //   - not found: value e1   - not found: value C1
    case C2(e2) => e2 //    - not found: value e2   - not found: value C2
  }
}

trait Container[TKey]
{
    abstract trait CB
    case class C1(val e : AnyRef) extends CB
    case class C2(val e : AnyRef) extends CB
}

Merci!

Était-ce utile?

La solution

Bien ... Les classes intérieures de Scala sont un peu délicates. Essayons un exemple simple avant de vous montrer la version réécrite du code que vous avez fourni.

case class Foo(x: Int) {
  case class Bar(y: String) 
}

Maintenant, considérons l'extrait de code suivant:

val x = new Foo(1)
val y = new Foo(2)

val a = new x.Bar("one")
val b = new y.Bar("two") 

Le type le plus générique de a et de b est Foo # Bar , ce qui signifie que la classe interne Bar avec n'importe quel objet externe de type Foo . Mais nous pourrions être plus précis en disant que le type de a est x.Bar et le type de b est y.Bar - ce qui signifie que a est une instance de la classe interne Bar avec l'objet externe x , similaire pour b .

Vous pouvez réellement voir que les types sont différents en appelant typeOf (a) et typeOf (b) , où typeOf est un utilitaire méthode définie comme telle. (il donne juste le type de son argument par une assez belle inférence et un peu d'utilisation de Manifest s)

def typeOf[T](x: T)(implicit m: scala.reflect.Manifest[T]) = m.toString

Lorsqu'un objet interne contient une référence à son objet englobant, vous ne pouvez pas instancier un objet interne sans en spécifier en quelque sorte l'objet externe. Par conséquent, vous pouvez appeler new x.Bar ("un") mais vous ne pouvez pas appeler new Foo # Bar ("?") - comme dans le second cas vous n'avez pas spécifié l'objet interne du nouvel objet que vous essayez de construire.

Revenons donc à votre extrait de code. Lorsque vous effectuez une recherche de motif, vous appelez en réalité un constructeur, lorsque vous appelez C1 (e1) . Comme C1 est un alias pour Container [TKey] # C1 vous avez essayé d'appeler un constructeur d'une classe interne sans spécifier son objet externe, ce qui échoue pour les raisons décrites ci-dessus. La manière dont j'écrirais le code serait la suivante:

trait Container[TKey] {
    abstract trait CB
    case class C1(val e : AnyRef) extends CB
    case class C2(val e : AnyRef) extends CB
}

class DoStuff[TKey] (val c: Container[TKey], val element: Container[TKey]#CB) {
  element match {
    case c.C1(e1) => Some(e1)
    case c.C2(e2) => Some(e2)
    case _        => None
  }
}

Maintenant, ceci compile et si tout va bien il fait ce que vous voulez. Mais prenez ceci avec le plus grand soin! En raison de la suppression du type, Scala ne peut pas garantir que l'élément est en réalité de type c.CB ou de type d.CB , où CB dans le cas de c et de d se trouvent être identiques.

Considérez cet exemple:

def matcher(arg: Foo#Bar) = {
  arg match {
    case x.Bar(n) => println("x");
    case y.Bar(n) => println("y");
  }
}

x et y sont les mêmes que précédemment. Essayez d’exécuter ce qui suit:

matcher(a)
matcher(b) 

Ils ont tous deux imprimé x !

Par conséquent, je voudrais réécrire le code pour qu'il y ait explicitement un élément dans le conteneur:

trait Container[TKey] {
    abstract trait CB
    case class C1(val e : AnyRef) extends CB
    case class C2(val e : AnyRef) extends CB
    val element: CB
}

class DoStuff[TKey](val c: Container[TKey]) {
  c.element match {
    case c.C1(e1) => Some(e1)
    case c.C2(e2) => Some(e2)
    case _        => None
  }
}

J'espère que ça aide:)

- Flaviu Cipcigan

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top