Pattern matching su due tipi simili

4

Qual è il modo migliore per gestire la corrispondenza dei pattern nella seguente situazione?

sealed trait Metadata
final case class Metadata1() extends Metadata
final case class Metadata2() extends Metadata
final case class Metadata3() extends Metadata

sealed trait Data
final case class Data1() extends Data
final case class Data2() extends Data
final case class Data3() extends Data

(metadata, data) match {
    case (Metadata1(), Data1()) =>
    case (Metadata2(), Data2()) =>
    case (Metadata3(), Data3()) =>
}

In questo esempio, i metadati di solito rimangono gli stessi, ma i dati possono essere aggiornati frequentemente.

Normalmente un compilatore si lamenterebbe se c'è un caso che non è gestito. Tuttavia, le situazioni in cui, ad esempio, i metadati sono Metadata1 e b è Data2 sarebbero considerate una condizione di errore e dovrebbero essere tutte gestite dallo stesso gestore di errori. Se aggiungiamo un catch all case, il compilatore non darà alcun avvertimento se Metadata4 e Data4 sono aggiunti. Qual è il modo migliore per gestire una situazione come questa?

    
posta MI3Guy 29.03.2015 - 14:43
fonte

1 risposta

3

Chiami i tipi "simili", ma sono solo simili a te. Il compilatore non sa nulla di questa somiglianza, quindi o hai bisogno del compilatore per apprenderlo o per alleviare in qualche modo. Ecco alcune possibili soluzioni che prenderei in considerazione in questa situazione:

Morde il proiettile e elenca tutti i casi (ovvero quello rapido)

Prima di considerare le possibilità più coinvolgenti, ricorda che puoi sempre semplicemente elencare tutti i casi. Ci sono% casi dim * n lì, quindi dovrebbe essere gestibile fino a 4 o forse 5 casi ciascuno se si strappa abbastanza.

In F # hai qualcosa chiamato pattern attivi che ti consente di definire un modo personalizzato per dividere il dominio con cui stai confrontando. Puoi usarlo per incapsulare la parte di corrispondenza dettagliata e riutilizzarla nelle corrispondenze successive. Scala ha estrattori che presumibilmente sono paragonabili ai modelli attivi.

Certamente questo non si adatta bene.

Aggiungi il tipo mancante (ovvero quello corretto)

L'osservazione che i tipi MetadataX e DataX sono apparentemente simili, ma il compilatore non ne sa nulla, implicherebbe che ci sia un tipo intermedio che manca all'immagine. È possibile introdurre quel tipo e farà semplicemente scomparire l'intero problema di corrispondenza.

type DataSet =
    | DataSet1 of Metadata1 * Data1
    | DataSet2 of Metadata2 * Data2
    | DataSet3 of Metadata3 * Data3

Naturalmente, ciò che rende le cose più facili dipende da come costruirai le istanze di quel tipo. Immagino che potresti avere tutto il Metadatas a portata di mano, ed è solo questione di ottenere quello giusto per il Data che ti viene consegnato, ma avrei davvero bisogno di saperne di più dell'immagine qui.

Suddividi la corrispondenza (ovvero quella pragmatica)

Se ci sono troppi casi da gestire e non è possibile aggiungere il tipo mancante per qualche motivo, è sempre possibile ristrutturare l'espressione della corrispondenza. Avresti bisogno di estrarre una logica comune nelle funzioni e perderai un po 'di leggibilità, ma almeno ti aggiri nell'esplosione combinatoria dei casi e continui a mantenere gli avvertimenti "caso non gestito". Il codice è in F #, ma puoi trattarlo come pseudo-codice (meno verboso di Scala comunque):

match data with
| Data1 d ->
    match metadata with 
    | Metadata1 md -> (* the proper logic goes here *)
    | other        -> (* handle the unmatched types here using a common function *)
| Data2 d ->
    match metadata with 
    | Metadata2 md -> (* the proper logic goes here *)
    | other        -> (* handle the unmatched types here using a common function *)
(...)

Con questo, hai ancora una corrispondenza esauriente sui casi di Data , e le corrispondenze annidate danno solo un leggero sovraccarico a ciascun caso.

Immagino che potrebbe essere reso ancora più bello rifasando le corrispondenze annidate in funzioni che scelgono una delle due continuazioni a seconda di un controllo booleano se i tipi Data e Metadata si adattano insieme.

    
risposta data 09.04.2015 - 11:41
fonte

Leggi altre domande sui tag