Perché il compilatore Scala non può dare un avviso di corrispondenza dei modelli per classi / tratti non sigillati?


10

Se uso un un seal traito abstract classin Scala e poi uso pattern matching, mi chiedo, il compilatore non sa al momento della compilazione per questo particolare patternmatch quali possibili implementazioni di questo tratto / classe sono disponibili? Quindi, in caso affermativo, non potrebbe dare avvisi di corrispondenza del modello anche se il trait/ abstract classnon è sigillato perché sa quali tipi potrebbero essere usati, controllando tutte le possibili dipendenze / importazioni?

Ad esempio se ho un Option[A]e faccio pattern matching solo per Some[A]ma non per None, il compilatore si lamenterà, perché Optionè sigillato.

Se il compilatore non può saperlo / risolverlo, perché non può? E se il compilatore (teoricamente) può farlo, quali sono i motivi per cui non viene utilizzato in Scala? Ci sono altre lingue che supportano quel tipo di comportamento?


Non è chiaro cosa stai chiedendo. Vuoi che il compilatore emetta un avviso se l'espressione di corrispondenza non copre tutti i possibili input? Forse un esempio renderebbe più chiara la tua domanda.
kdgregory,

2
Se qualcuno può introdurre una nuova sottoclasse, la corrispondenza del modello non può mai essere esaustiva. Ad esempio, produci una classe astratta Foocon sottoclassi A, Be C, e tutte le tue corrispondenze di pattern corrispondono solo a quelle tre. Niente mi impedisce di aggiungere una nuova sottoclasse Dche farà esplodere le tue combinazioni di schemi.
Doval,

@kdgregory Sì, ce l'hai. Ho aggiunto un esempio per renderlo più chiaro.
valenterry,

3
Il controllo di tutte le importazioni non è sufficiente per scoprire tutte le possibili sottoclassi. Un'altra sottoclasse potrebbe essere dichiarata in un file di classe separato che verrà successivamente caricato durante il runtime tramite java.lang.ClassLoader.
amon

Risposte:


17

Capire tutte le sottoclassi di una classe si chiama Class Hierarchy Analysis e fare CHA statico in un linguaggio con caricamento di codice dinamico equivale a risolvere il problema di Halting.

Inoltre, uno degli obiettivi di Scala è la compilazione e la distribuzione separate di moduli indipendenti, quindi il compilatore semplicemente non può sapere se una classe è sottoclassata in un altro modulo, perché non guarda mai più di un modulo. (Dopotutto, potresti compilare un modulo sull'interfaccia di qualche altro modulo senza che quel modulo esista sul tuo sistema!) Ecco perché sealedrichiede che tutte le sottoclassi siano definite nella stessa unità di compilazione.

Questo è anche uno dei motivi per cui le JVM possono competere in modo così favorevole con i compilatori C ++: i compilatori C ++ sono in genere compilatori statici, quindi non possono in generale capire se un metodo viene sovrascritto o meno e quindi non possono incorporarlo. Le JVM OTOH, in genere sono compilatori dinamici, non hanno bisogno di eseguire CHA per capire se un metodo viene ignorato o meno, possono solo guardare la Gerarchia di Classe in fase di esecuzione. E anche se in un secondo momento nell'esecuzione del programma arriva una nuova sottoclasse che non c'era prima, niente di grave, ricompilare quel pezzo di codice senza incorporare.

Nota: tutto ciò si applica solo all'interno di Scala. JVM non ne ha idea sealed, quindi è perfettamente possibile sottoclassare le sealedclassi da un'altra lingua JVM, poiché non c'è modo di comunicarlo in un'altra lingua. La sealedproprietà è registrata nell'annotazione ScalaSig, ma i compilatori di altre lingue non tengono conto di tali annotazioni, ovviamente.


3

Esso può essere fatto (almeno per tutte le classi noti al momento della compilazione), è solo costoso. Distruggeresti completamente la compilazione incrementale, perché tutto ciò che contiene una corrispondenza di pattern dovrebbe effettivamente essere ricompilato ogni volta che qualsiasi altro file viene modificato.

E cosa stai comprando? È un odore di codice scrivere combinazioni di pattern che devono cambiare frequentemente quando viene aggiunta una nuova classe derivata. È una violazione del principio aperto / chiuso . Usa l'ereditarietà correttamente e non avrai bisogno di scrivere quel tipo di corrispondenze di modelli. E sì, il principio di apertura / chiusura si applica anche ai linguaggi funzionali senza eredità basata sulla classe. Infatti tra funzionalità come classi di tipi, multimetodi e semplici funzioni di ordine superiore, i linguaggi funzionali rendono l'estensione senza modifiche molto più semplice.


1
It can be done (at least for all classes known at compile time), it's just expensive.Ma se il programma non è autonomo al 100% (cioè dipende da .jarfile esterni ), non potresti intrufolarti in una nuova sottoclasse dopo la compilazione attraverso una delle jars? Quindi il compilatore potrebbe dirti "Le tue corrispondenze di schemi sono esaustive ora, ma ciò potrebbe cambiare se una qualsiasi delle tue dipendenze cambia", il che è abbastanza inutile poiché il punto di avere dipendenze esterne è di poterle aggiornare senza ricompilare!
Doval,

Da qui il disclaimer. In pratica, se tu fossi abbastanza accoppiato per aver bisogno di una corrispondenza esaustiva su una dipendenza, esterna o di altro tipo, ti consigliamo di ricompilare comunque.
Karl Bielefeldt,

@Doval Quindi dovresti usare una qualche forma di delega e lasciare che la classe chiamata decida cosa fare e invertire il controllo. Questo è ciò per cui OOP era destinato. Se non lo desideri, hai il tuo problema attuale.
Slitta,

@ArtB Non riesco a vedere che delega o inversione di controllo hanno a che fare con questo.
Doval,

Se vuoi che funzioni con le persone che possono aggiungerlo esternamente, chiama la classe e sposta la logica in quelle classi.
Slitta,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.