La classe Scala Case corrisponde all'errore di compilazione con tipi interni con alias?

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

  •  06-07-2019
  •  | 
  •  

Domanda

Come si usa la corrispondenza della classe case con tipi con alias? Questo funziona quando estraggo CB ecc. Dal contenitore.

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
}

Grazie!

È stato utile?

Soluzione

Giusto ... Le classi interne in Scala sono un po 'complicate. Proviamo un semplice esempio prima di mostrarti la versione riscritta del codice che hai fornito.

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

Ora considera il seguente frammento di codice:

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

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

Il tipo più generico di a e b è Foo # Bar , che significa la classe interna Bar con qualsiasi oggetto esterno di tipo Foo . Ma potremmo essere più specifici nel dire che il tipo di a è x.Bar e il tipo di b è y.Bar - il che significa che a è un'istanza della classe interna Bar con l'oggetto esterno x , simile per b .

Puoi effettivamente vedere che i tipi sono diversi chiamando typeOf (a) e typeOf (b) , dove typeOf è un'utilità metodo definito come tale. (fornisce solo il tipo del suo argomento con una deduzione del tipo abbastanza piacevole e un po 'di utilizzo di Manifest s)

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

Poiché un oggetto interno contiene un riferimento al suo oggetto che lo racchiude, non è possibile creare un'istanza di un oggetto interno senza in qualche modo specificarne l'oggetto esterno. Pertanto, puoi chiamare new x.Bar (" one ") ma non puoi chiamare new Foo # Bar ("? & Quot;) - come nel secondo caso non hai specificato qual è l'oggetto interno per il nuovo oggetto che provi a costruire.

Quindi, torniamo allo snippet di codice. Quando corrispondi al modello, in realtà stai chiamando un costruttore - quando chiami C1 (e1) . Poiché C1 è un alias per Container [TKey] # C1 hai provato a chiamare un costruttore di una classe interna senza specificare il suo oggetto esterno, che fallisce a causa dei motivi sopra indicati. Il modo in cui scriverei il codice sarebbe il seguente:

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

Ora questo si compila e, si spera, fa quello che vuoi. Ma prendilo con molta cura! A causa della cancellazione del tipo, Scala non può garantire che l'elemento sia effettivamente di tipo c.CB o di tipo d.CB dove CB nel caso di c e d sembra essere lo stesso.

Considera questo esempio:

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

dove x e y sono come prima. Prova a eseguire quanto segue:

matcher(a)
matcher(b) 

Entrambi stampano x !

Pertanto riscriverei il codice per avere esplicitamente un elemento nel contenitore:

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

Spero che sia d'aiuto :)

- Flaviu Cipcigan

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top