2015-12-22 11 views
5

Ho una gerarchia di caso di classe per codificare alcuni di richiesta e di elaborazione errori:Scala pattern match non è esaustivo su classi case annidate

sealed trait OpError 
    sealed trait RequestErrorType 
    sealed trait ProcessingErrorType 

    final case class InvalidEndpoint(reason: String) extends RequestErrorType 
    final case class InvalidParameters(reason: String) extends RequestErrorType 

    final case class InvalidFormat(response: String) extends ProcessingErrorType 
    final case class EntityNotFound(id: Long) extends ProcessingErrorType 

    final case class RequestError(errorType: RequestErrorType) extends OpError 
    final case class ProcessingError(errorType: ProcessingErrorType) extends OpError 

Se scrivo un semplice incontro in tutti i modelli:

def printMatches(error: OpError): Unit = error match { 
    case RequestError(InvalidEndpoint(reason)) => //print something 
    case RequestError(InvalidParameters(reason)) => //print something 
    case ProcessingError(InvalidFormat(format)) => //print something 
    case ProcessingError(EntityNotFound(entityId)) => //print something 
    } 

il compilatore mi dà un avvertimento circa partita mancante:

match may not be exhaustive. 
It would fail on the following input: ProcessingError(_) 
def printMatches(error: OpError): Unit = error match { 

Ma ProcessingError prende in un P rocessingErrorType con solo due estensioni: InvalidFormat e EntityNotFound, entrambe le quali sono contabilizzate nella corrispondenza del modello. Cosa mi manca?

Ancora più curioso è che se cambio il tipo di parametro di InvalidParameters o InvalidEndpoint ad una stringa *, non ottengo l'errore:

final case class InvalidParameters(reason: String*) extends RequestErrorType 

Tutte le idee?

+0

'printMatches (ProcessingError (new ProcessingErrorType {}))' non corrisponde ad alcun –

+0

questo esempio vìola il contratto 'sealed'. – pedrofurla

+0

In termini di comportamento curioso con il parametro String *, sembra che Scala giri di controllo esaustivo quando una classe case ha un parametro varargs: https://issues.scala-lang.org/browse/SI-8178?jql = etichette% 20% 3D% 20exhaustiveness – ssanj

risposta

4

Questo è un bug confermato. I filed a bug report for this ed è stato fissato per Scala 2.12.0-M4.

+0

vedere questo commento per una soluzione: https://issues.scala-lang.org/browse/SI-9630?focusedCommentId=73864&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment- 73.864 – ssanj

0

si può aiutare il compilatore con un'annotazione incontrollato:

... = (error: @unchecked) match ... 

ma si dovrebbe essere sicuri, il tuo partner è esaustivo.

+0

true, ma volevo che il compilatore mi aiutasse a capire le istanze non valide al contrario. ;) – ssanj

0

Penso che la corrispondenza di esaustività funzioni su un singolo livello di ereditarietà. RequestErrorType e ProcessingErrorType fanno parte del costruttore in cui tale esaustività non viene controllata.

È possibile vederlo dalla lettura del codice, ma sembra che il compilatore non lo faccia.

+0

Dire che commento la corrispondenza per RequestError (InvalidParameters (reason)) da printMatches, quindi il compilatore mi fa sapere che mi manca quella variazione esatta: "la corrispondenza potrebbe non essere esaustiva. Fallirebbe sul seguente input: RequestError (InvalidParameters (_))". Quindi immagino che il compilatore possa capire qualche ereditarietà annidata. – ssanj

1

Molto interessante! Sfortunatamente, non ho trovato una risposta. Sto girando intorno allo http://www.scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html#constructor-patterns ma non ho trovato una spiegazione valida per quello che sta succedendo.

Ecco una demo più semplice (spero che non ti dispiaccia):

sealed abstract class ClassOne 
case class ClassOneImpl() extends ClassOne 

sealed abstract class ClassTwo() 
case class ClassTwoImpl() extends ClassTwo 

sealed abstract class Foo 
case class FooOne(x: ClassOne) extends Foo 
case class FooTwo(x: ClassTwo) extends Foo 

def printMatches(st: Foo): Unit = st match { 
    case FooOne(ClassOneImpl()) => println() 
    case FooTwo(ClassTwoImpl()) => println() 
} 

ho osservato che ciascuno dei seguenti due modifiche rimuove l'avvertimento:
1) Modifica FooOne e FooTwo firme in modo che invece di prendere ClassOne e ClassTwo prendono ClassOneImpl e ClassTwoImpl
2) Rimozione FooOne o FooTwo modo che ci sia una sola classe case estendentesi Foo (che porta t o un solo caso nel pattern matching).

Forse potremmo inviare un problema e vedere cosa dicono?

+1

L'ho archiviato come un bug https://issues.scala-lang.org/browse/SI-9630 e da allora è stato risolto e chiuso! Sìì! :) – ssanj

Problemi correlati