Pendant de nombreuses années de programmation OO, j'ai compris ce que sont les syndicats discriminés, mais je ne les ai jamais vraiment ratés. J'ai récemment fait de la programmation fonctionnelle en C # et maintenant je trouve que j'aimerais continuer à les avoir. Cela me déconcerte car, à première vue, le concept d'unions discriminées semble tout à fait indépendant de la dichotomie fonctionnelle / OO.
Y a-t-il quelque chose d'inhérent à la programmation fonctionnelle qui rend les syndicats discriminés plus utiles qu'ils ne le seraient en OO, ou est-ce qu'en me forçant à analyser le problème d'une «meilleure» manière, j'ai simplement relevé mes normes et maintenant j'exige une meilleure modèle?
Réponses:
Les unions discriminées brillent vraiment en conjonction avec la correspondance de modèles, où vous sélectionnez un comportement différent selon les cas. Mais ce modèle est fondamentalement antithétique aux principes OO purs.
En OO pur, les différences de comportement doivent être définies par les types (objets) eux-mêmes et encapsulées. Ainsi, l'équivalence de la correspondance de motifs serait d'appeler une seule méthode sur l'objet lui-même, qui est ensuite surchargée par les sous-types en question pour définir un comportement différent. L'inspection du type d'un objet de l'extérieur (ce que fait la correspondance de motifs) est considérée comme un contre-modèle.
La différence fondamentale est que les données et le comportement sont séparés dans la programmation fonctionnelle, tandis que les données et le comportement sont encapsulés ensemble dans OO.
Telle est la raison historique. Un langage comme C # évolue d'un langage OO classique à un langage multi-paradigmes en incorporant de plus en plus de fonctionnalités.
la source
List<A>
la méthodeB Match<B>(B nil, Func<A,List<A>,B> cons)
. Par exemple, c'est exactement le modèle que Smalltalk utilise pour les booléens. C'est aussi essentiellement la façon dont Scala le gère. Le fait que plusieurs classes soient utilisées est un détail d'implémentation qui n'a pas besoin d'être exposé.isEmpty
méthode qui vérifie si la référence au nœud suivant est nulle.Ayant programmé en Pascal et Ada avant d'apprendre la programmation fonctionnelle, je n'associe pas les unions discriminées à la programmation fonctionnelle.
Les syndicats discriminés sont en quelque sorte le double de l'héritage. Le premier permet d'ajouter facilement des opérations sur un ensemble fixe de types (ceux de l'union), et l'héritage permet d'ajouter facilement des types avec un ensemble fixe d'opérations. (Comment ajouter facilement les deux s'appelle le problème d'expression ; c'est un problème particulièrement difficile pour les langues avec un système de type statique.)
En raison de l'accent mis par OO sur les types et du double accent de la programmation fonctionnelle sur les fonctions, les langages de programmation fonctionnels ont une affinité naturelle pour les types d'union et offrent des structures syntaxiques pour faciliter leur utilisation.
la source
Les techniques de programmation impératives, souvent utilisées en OO, reposent souvent sur deux modèles:
null
pour indiquer «aucune valeur» ou échec.Le paradigme fonctionnel évite généralement les deux, préférant renvoyer un type composé qui indique une raison de succès / échec ou une valeur / aucune valeur.
Les syndicats discriminés font l'affaire pour ces types de composés. Par exemple, dans la première instance, vous pouvez renvoyer
true
ou une structure de données décrivant l'échec. Dans le deuxième cas, une union qui contient une valeur, ounone
,nil
etc. Le deuxième cas est si courant que de nombreux langages fonctionnels ont un type "peut-être" ou "option" intégré pour représenter cette union valeur / aucune.Lorsque vous passez à un style fonctionnel avec, par exemple, C #, vous trouverez rapidement un besoin pour ces types de composés.
void/throw
etnull
ne vous sentez pas bien avec un tel code. Et les syndicats discriminés (UD) convenaient bien au projet de loi. Ainsi, vous vous êtes retrouvé à les vouloir, comme beaucoup d'entre nous.La bonne nouvelle est qu'il existe de nombreuses bibliothèques qui modélisent les DU en C # par exemple (jetez un œil à ma propre bibliothèque Succinc <T> par exemple).
la source
Les types de somme seraient généralement moins utiles dans les langages OO traditionnels car ils résolvent un type de problème similaire au sous-typage OO. Une façon de les regarder est qu'ils gèrent tous les deux le sous-typage, mais OO est
open
c'est -à- dire qu'on peut ajouter des sous-types arbitraires à un type parent et les types de somme sontclosed
c'est-à - dire qu'on détermine à l'avance quels sous-types sont valides.Maintenant, de nombreux langages OO combinent le sous-typage avec d'autres concepts tels que les structures héritées, le polymorphisme, le typage de référence, etc. pour les rendre généralement plus utiles. Une conséquence est qu'ils ont tendance à être plus de travail à mettre en place (avec des classes et des constructeurs et ainsi de suite) donc ils ne sont généralement pas utilisés pour des choses comme
Result
s etOption
s et ainsi de suite jusqu'à ce que le typage générique devienne courant.Je dirais également que l'accent mis sur les relations réelles que la plupart des gens ont appris lorsqu'ils ont commencé à programmer OO, par exemple Dog isa Animal, signifiait que Integer isa Result ou Error isa Result semble un peu étranger. Bien que les idées soient assez similaires.
Quant aux raisons pour lesquelles les langages fonctionnels pourraient préférer la frappe fermée à la frappe ouverte, l'une des raisons possibles est qu'ils ont tendance à préférer la correspondance de modèles. Ceci est utile pour le polymorphisme de fonction mais il fonctionne également très bien avec les types fermés car le compilateur peut vérifier statiquement que la correspondance couvre tous les sous-types. Cela peut rendre la langue plus cohérente même si je ne pense pas qu'il y ait un avantage inhérent (je peux me tromper).
la source
sealed
signifie "ne peut être étendu que dans la même unité de compilation", ce qui vous permet de fermer l'ensemble des sous-classes au moment du design.Swift utilise volontiers les syndicats discriminants, sauf qu'il les appelle des "énumérations". Les énumérations sont l'une des cinq catégories fondamentales d'objets dans Swift, qui sont la classe, la structure, l'énumération, le tuple et la fermeture. Les options Swift sont des énumérations qui sont des unions discriminantes, et elles sont absolument essentielles pour tout code Swift.
Ainsi, la prémisse selon laquelle "les unions discriminatoires sont associées à une programmation fonctionnelle" est fausse.
la source