Pourquoi le compilateur Scala ne peut-il pas donner d'avertissement de correspondance de modèle pour les classes / traits non scellés?

10

Si j'utilise un non scellé traitou abstract classdans Scala et que j'utilise ensuite la correspondance de modèles, je me demande si le compilateur ne sait pas au moment de la compilation pour ce patternmatch particulier quelles implémentations possibles de ce trait / classe sont disponibles? Donc, si c'est le cas, ne pourrait-il pas donner des avertissements de correspondance de modèle même si le trait/ abstract classn'est pas scellé parce qu'il sait quels types pourraient être utilisés, en vérifiant toutes les dépendances / importations possibles?

Par exemple, si j'ai un Option[A]et que je fais une correspondance de modèle uniquement pour Some[A]mais pas pour None, le compilateur se plaindra, car il Optionest scellé.

Si le compilateur ne peut pas savoir / résoudre cela, pourquoi ne le peut-il pas? Et si le compilateur (théoriquement) peut le faire, quelles sont les raisons pour lesquelles il n'est pas utilisé dans Scala? Y a-t-il d'autres langues qui prennent en charge ce type de comportement?

valenterry
la source
On ne sait pas exactement ce que vous demandez. Voulez-vous que le compilateur émette un avertissement si l'expression de correspondance ne couvre pas toutes les entrées possibles? Peut-être qu'un exemple rendrait votre question plus claire.
kdgregory
2
Si quelqu'un peut introduire une nouvelle sous-classe, la correspondance de modèle ne peut jamais être exhaustive. Par exemple, vous produisez une classe abstraite Fooavec des sous A- classes,, Bet C, et toutes vos correspondances de modèle ne correspondent qu'à ces trois. Rien ne m'empêche d'ajouter une nouvelle sous D- classe qui fera exploser vos correspondances de motifs.
Doval
@kdgregory Oui, vous l'avez. J'ai ajouté un exemple pour le rendre plus clair.
valenterry
3
La vérification de toutes les importations n'est pas suffisante pour découvrir toutes les sous-classes possibles. Une autre sous-classe peut être déclarée dans un fichier de classe distinct qui sera ensuite chargé lors de l'exécution via java.lang.ClassLoader.
amon

Réponses:

17

La définition de toutes les sous-classes d'une classe est appelée analyse de la hiérarchie des classes, et effectuer un CHA statique dans un langage avec chargement de code dynamique équivaut à résoudre le problème de l'arrêt.

De plus, l'un des objectifs de Scala est la compilation et le déploiement séparés de modules indépendants, de sorte que le compilateur ne peut tout simplement pas savoir si une classe est sous-classée dans un autre module, car il ne regarde jamais plus d'un module. (Après tout, vous pouvez compiler un module par rapport à l'interface d'un autre module sans que ce module n'existe même sur votre système!) C'est pourquoi sealedtoutes les sous-classes doivent être définies dans la même unité de compilation.

C'est également l'une des raisons pour lesquelles les machines virtuelles Java peuvent rivaliser si favorablement avec les compilateurs C ++: les compilateurs C ++ sont généralement des compilateurs statiques, de sorte qu'ils ne peuvent généralement pas déterminer si une méthode est remplacée ou non, et ne peuvent donc pas l'intégrer. Les JVM OTOH sont généralement des compilateurs dynamiques, ils n'ont pas besoin d'effectuer CHA pour déterminer si une méthode est remplacée ou non, ils peuvent simplement regarder la hiérarchie des classes au moment de l'exécution. Et même si, à un stade ultérieur de l'exécution du programme, une nouvelle sous-classe apparaît, qui n'était pas là auparavant, ce n'est pas grave, recompilez simplement ce morceau de code sans l'inliner.

Remarque: tout cela ne s'applique qu'à Scala. La JVM n'a aucune notion de sealed, il est donc parfaitement possible de sous-classer les sealedclasses d' un autre langage JVM, car il n'y a aucun moyen de le communiquer à un autre langage. La sealedpropriété est enregistrée dans l' ScalaSigannotation, mais les compilateurs d'autres langages ne tiennent pas compte de ces annotations, évidemment.

Jörg W Mittag
la source
3

Cela peut être fait (au moins pour toutes les classes connues au moment de la compilation), c'est juste cher. Vous détruiriez complètement la compilation incrémentielle, car tout ce qui contient une correspondance de modèle devrait effectivement être recompilé à chaque changement de fichier.

Et qu'achetez-vous? C'est une odeur de code pour écrire des correspondances de modèle qui doivent changer fréquemment lorsqu'une nouvelle classe dérivée est ajoutée. C'est une violation du principe ouvert / fermé . Utilisez l'héritage correctement et vous n'aurez pas besoin d'écrire ces types de correspondances de modèle. Et oui, le principe ouvert / fermé s'applique également aux langages fonctionnels sans héritage basé sur les classes. En fait, entre des fonctionnalités telles que les classes de types, les méthodes multiples et les fonctions de niveau supérieur, les langages fonctionnels facilitent l'extension sans modification.

Karl Bielefeldt
la source
1
It can be done (at least for all classes known at compile time), it's just expensive.Mais si le programme n'est pas 100% autonome (c'est-à-dire qu'il dépend de .jarfichiers externes ), ne pourriez-vous pas vous faufiler dans une nouvelle sous-classe après la compilation via l'un des jars? Ainsi, le compilateur pourrait vous dire "Vos correspondances de modèles sont exhaustives maintenant, mais cela peut changer si l'une de vos dépendances change", ce qui est assez inutile car le point d'avoir des dépendances externes est de pouvoir les mettre à jour sans recompiler!
Doval
D'où l'avertissement. En pratique, si vous étiez suffisamment couplé pour avoir besoin d'une correspondance exhaustive sur une dépendance, externe ou autre, vous voudriez quand même recompiler.
Karl Bielefeldt
@Doval Ensuite, vous devez utiliser une forme de délégation et laisser la classe appelée décider quoi faire et inverser le contrôle. C'est à cela que la POO était destinée. Si vous ne voulez pas cela, vous avez votre problème actuel.
Traîneau du
@ArtB Je ne vois pas ce que la délégation ou l'inversion de contrôle a à voir avec cela.
Doval
Si vous souhaitez qu'il fonctionne avec des personnes pouvant y ajouter des éléments externes, appelez la classe et déplacez la logique dans ces classes.
Traîneau du