Je suis en train de développer un langage compilé de manière statique et fortement typée, et je revisite l'idée d'inclure ou non la surcharge de fonctions comme fonctionnalité de langage. J'ai réalisé que je suis un peu biaisé, venant principalement d'un C[++|#]
arrière - plan.
Quels sont les arguments les plus convaincants pour et contre l' inclusion de la surcharge de fonctions dans un langage?
EDIT: Y a - t-il personne qui a une opinion opposée?
Bertrand Meyer (créateur d'Eiffel en 1985/1986) appelle une méthode surchargeant ceci: (source)
un mécanisme de vanité qui n'apporte rien à la puissance sémantique d'un langage OO, mais entrave la lisibilité et complique la tâche de chacun
Maintenant, ce sont des généralisations radicales, mais c'est un gars intelligent, donc je pense qu'il est sûr de dire qu'il pourrait les sauvegarder s'il en avait besoin. En fait, il avait presque convaincu Brad Abrams (l'un des développeurs CLSv1) que .NET ne devait pas prendre en charge la surcharge de méthode. (source) C'est quelque chose de puissant. Quelqu'un peut-il éclairer ses pensées et savoir si son point de vue est toujours justifié 25 ans plus tard?
la source
Je recommande au moins de connaître les classes de types dans Haskell. Les classes de types ont été créées pour être une approche disciplinée de la surcharge des opérateurs, mais ont trouvé d'autres utilisations et, dans une certaine mesure, ont fait de Haskell ce qu'il est.
Par exemple, voici un exemple de surcharge ad-hoc (pas tout à fait valide Haskell):
Et voici le même exemple de surcharge avec des classes de type:
Un inconvénient est que vous devez trouver des noms funky pour toutes vos classes de types (comme dans Haskell, vous avez la plutôt abstraite
Monad
,Functor
,Applicative
ainsi que le plus simple et plus reconnaissableEq
,Num
etOrd
).Un avantage est que, une fois que vous êtes familiarisé avec une classe de types, vous savez comment utiliser n'importe quel type de cette classe. De plus, il est facile de protéger les fonctions des types qui n'implémentent pas les classes nécessaires, comme dans:
Edit: Dans Haskell, si vous vouliez un
==
opérateur qui accepte deux types différents, vous pouvez utiliser une classe de type multi-paramètres:Bien sûr, c'est probablement une mauvaise idée, car cela vous permet explicitement de comparer des pommes et des oranges. Cependant, vous voudrez peut-être considérer cela
+
, car ajouter unWord8
àInt
est vraiment une chose sensée à faire dans certains contextes.la source
(==) :: Int -> Float -> Bool
n'importe où? (bien que ce soit une bonne idée, bien sûr)class Eq a ...
traduit en pseudo-famille C seraitinterface Eq<A> {bool operator==(A x, A y);}
, et au lieu d'utiliser du code basé sur des modèles pour comparer des objets arbitraires, vous utilisez cette «interface». Est-ce correct?==
d'être dans un espace de noms différent mais ne permet pas de le remplacer. Veuillez noter qu'un seul espace de noms (Prelude
) est inclus par défaut mais vous pouvez empêcher le chargement en utilisant des extensions ou en l'important explicitement (import Prelude ()
n'importera rienPrelude
etimport qualified Prelude as P
n'insérera pas de symboles dans l'espace de noms actuel).Autoriser la surcharge de fonctions, vous ne pouvez pas faire ce qui suit avec des paramètres facultatifs (ou si vous le pouvez, pas bien).
exemple trivial suppose aucune
base.ToString()
méthodela source
J'ai toujours préféré les paramètres par défaut à la surcharge de fonctions. Les fonctions surchargées appellent généralement une version "par défaut" avec des paramètres par défaut. Pourquoi écrire
Quand je pourrais faire:
Cela dit, je réalise que les fonctions surchargées font des choses différentes, plutôt que d'appeler simplement une autre variante avec des paramètres par défaut ... mais dans un tel cas, ce n'est pas une mauvaise idée (en fait, c'est probablement un bonne idée) de lui donner juste un nom différent.
(De plus, les arguments de mots clés de style Python fonctionnent très bien avec les paramètres par défaut.)
la source
Array Slice(int start, int length) {...}
surchargéArray Slice(int start) {return this.Slice(start, this.Count - start);}
? Cela ne peut pas être codé à l'aide de paramètres par défaut. Pensez-vous qu'il faudrait leur donner des noms différents? Si oui, comment les nommeriez-vous?indexOf(char ch)
+indexOf(Date dt)
sur une liste. J'aime aussi les valeurs par défaut, mais elles ne sont pas interchangeables avec la frappe statique.Vous venez de décrire Java. Ou C #.
Pourquoi réinventes-tu la roue?
Assurez-vous que le type de retour fait partie de la signature de la méthode et ducontenu Overload to your hearts, il nettoie vraiment le code lorsque vous n'avez pas à le dire.la source
EnumX.Flag1 | Flag2 | Flag3
. Je ne mettrai cependant pas cela en œuvre. Si je l'ai fait et que le type de retour n'a pas été utilisé, je rechercherais un type de retour devoid
.Grrr .. pas encore assez de privilège pour commenter ..
@Mason Wheeler: Faites attention alors à Ada, qui surcharge le type de retour. De plus, mon langage Felix le fait aussi dans certains contextes, en particulier lorsqu'une fonction renvoie une autre fonction et qu'il y a un appel comme:
le type de b peut être utilisé pour la résolution de surcharge. Surcharge également C ++ sur le type de retour dans certaines circonstances:
En fait, il existe des algorithmes de surcharge sur le type de retour, utilisant l'inférence de type. Ce n'est pas si difficile à faire avec une machine, le problème est que les humains ont du mal. (Je pense que le plan est donné dans le Dragon Book, l'algorithme est appelé l'algorithme de bascule si je me souviens bien)
la source
Cas d'utilisation contre l'implémentation de la surcharge de fonctions: 25 méthodes portant le même nom qui font en quelque sorte les mêmes choses mais avec des ensembles d'arguments complètement différents dans une grande variété de modèles.
Cas d'utilisation contre l'implémentation de la surcharge de fonctions: 5 méthodes de même nom avec des ensembles de types très similaires dans le même modèle exact.
À la fin de la journée, je ne suis pas impatient de lire les documents pour une API produite dans les deux cas.
Mais dans un cas, il s'agit de ce que les utilisateurs pourraient faire. Dans l'autre cas, c'est ce que les utilisateurs doivent faire en raison d'une restriction de langue. OMI, il est préférable de permettre au moins la possibilité aux auteurs de programmes d'être suffisamment intelligents pour surcharger sensiblement sans créer d'ambiguïté. Lorsque vous vous giflez les mains et retirez l'option, vous garantissez essentiellement que l'ambiguïté va se produire. Je préfère faire confiance à l'utilisateur pour faire la bonne chose que de supposer qu'il fera toujours la mauvaise chose. D'après mon expérience, le protectionnisme a tendance à conduire à un comportement encore pire de la part d'une communauté linguistique.
la source
J'ai choisi de fournir une surcharge ordinaire et des classes de types multi-types dans mon langage Felix.
Je considère que la surcharge (ouverte) est essentielle, en particulier dans un langage qui a beaucoup de types numériques (Felix a tous les types numériques de C). Cependant, contrairement à C ++ qui abuse de la surcharge en faisant dépendre les modèles, le polymorphisme Felix est paramétrique: vous avez besoin d'une surcharge pour les modèles en C ++ car les modèles en C ++ sont mal conçus.
Les classes de type sont également fournies dans Felix. Pour ceux qui connaissent le C ++ mais qui ne plaisantent pas avec Haskell, ignorez ceux qui le décrivent comme une surcharge. Ce n'est pas comme une surcharge à distance, c'est plutôt comme une spécialisation de modèle: vous déclarez un modèle que vous n'implémentez pas, puis fournissez des implémentations pour des cas particuliers selon vos besoins. Le typage est paramétriquement polymorphe, l'implémentation se fait par instanciation ad hoc mais elle n'est pas destinée à être sans contrainte: elle doit implémenter la sémantique voulue.
Dans Haskell (et C ++), vous ne pouvez pas énoncer la sémantique. En C ++, l'idée "Concepts" est à peu près une tentative d'approximation de la sémantique. Dans Felix, vous pouvez approximer l'intention avec des axiomes, des réductions, des lemmes et des théorèmes.
Le principal, et seulement avantage de la surcharge (ouverte) dans un langage bien fondé sur des principes comme Felix est qu'il facilite la mémorisation des noms de fonction de bibliothèque, à la fois pour l'auteur du programme et pour le réviseur de code.
Le principal inconvénient de la surcharge est l'algorithme complexe requis pour l'implémenter. Cela ne correspond pas non plus très bien à l'inférence de type: bien que les deux ne soient pas entièrement exclusifs, l'algorithme pour faire les deux est suffisamment complexe, le programmeur ne serait probablement pas en mesure de prédire les résultats.
En C ++, c'est aussi un problème car il a un algorithme de correspondance bâclé et prend également en charge les conversions de type automatiques: dans Felix, j'ai "corrigé" ce problème en exigeant une correspondance exacte et aucune conversion de type automatique.
Vous avez donc un choix, je pense: surcharge ou inférence de type. L'inférence est mignonne, mais elle est également très difficile à mettre en œuvre d'une manière qui diagnostique correctement les conflits. Ocaml, par exemple, vous indique d'où il détecte un conflit, mais pas d'où il a déduit le type attendu.
La surcharge n'est pas beaucoup mieux, même si vous avez un compilateur de qualité qui essaie de vous dire tous les candidats, il peut être difficile de lire si les candidats sont polymorphes, et pire encore s'il s'agit de piratage de modèles C ++.
la source
Cela revient au contexte, mais je pense que la surcharge rend une classe beaucoup plus utile lorsque j'en utilise une écrite par quelqu'un d'autre. Vous vous retrouvez souvent avec moins de redondance.
la source
Si vous cherchez à avoir des utilisateurs familiarisés avec les langages de la famille C, alors oui, vous devriez le faire, car vos utilisateurs s'y attendront.
la source