Je ne connais pas tous les langages de programmation, mais il est clair que généralement la possibilité de surcharger une méthode en tenant compte de son type de retour (en supposant que ses arguments sont du même nombre et du même type) n'est pas prise en charge.
Je veux dire quelque chose comme ça:
int method1 (int num)
{
}
long method1 (int num)
{
}
Ce n'est pas que ce soit un gros problème de programmation, mais à certaines occasions, je l'aurais bien accueilli.
Évidemment, il n'y aurait aucun moyen pour ces langages de supporter cela sans moyen de différencier la méthode qui est appelée, mais la syntaxe pour cela peut être aussi simple que quelque chose comme [int] method1 (num) ou [long] method1 (num) de cette façon, le compilateur saurait qui serait celui qui serait appelé.
Je ne sais pas comment fonctionnent les compilateurs, mais cela ne semble pas si difficile à faire, alors je me demande pourquoi quelque chose comme ça n'est généralement pas implémenté.
Quelles sont les raisons pour lesquelles une telle chose n'est pas prise en charge?
la source
Foo
etBar
.Réponses:
Cela complique la vérification de type.
Lorsque vous autorisez uniquement la surcharge en fonction des types d'arguments et ne permettez que la déduction des types de variables à partir de leurs initialiseurs, toutes vos informations de type circulent dans une seule direction: vers le haut de l'arborescence de syntaxe.
Lorsque vous autorisez les informations de type à voyager dans les deux sens, par exemple en déduisant le type d'une variable de son utilisation, vous avez besoin d'un solveur de contraintes (tel que l'algorithme W, pour les systèmes de type Hindley-Milner) afin de déterminer le type.
Ici, nous devions laisser le type de
x
comme une variable de type non résolue∃T
, où tout ce que nous savons à ce sujet est qu'il est analysable. Ce n'est que plus tard, lorsqu'ilx
est utilisé sur un type concret, que nous avons suffisamment d'informations pour résoudre la contrainte et déterminer cela∃T = int
, qui propage les informations de type dans l'arborescence de syntaxe de l'expression d'appel versx
.Si nous ne parvenions pas à déterminer le type de
x
, soit ce code serait également surchargé (de sorte que l'appelant déterminerait le type), soit nous devions signaler une erreur concernant l'ambiguïté.À partir de là, un concepteur de langage peut conclure:
Cela ajoute de la complexité à la mise en œuvre.
Cela rend la vérification typographique plus lente - dans les cas pathologiques, de façon exponentielle.
Il est plus difficile de produire de bons messages d'erreur.
C'est trop différent du statu quo.
Je n'ai pas envie de le mettre en œuvre.
la source
Parce que c'est ambigu. En utilisant C # comme exemple:
Quelle surcharge devrions-nous utiliser?
Ok, c'était peut-être un peu injuste. Le compilateur ne peut pas déterminer quel type utiliser si nous ne le disons pas dans votre langage hypothétique. Ainsi, la frappe implicite est impossible dans votre langue et il y a des méthodes anonymes et Linq avec ...
Celui-ci, ça va? (Signatures légèrement redéfinies pour illustrer ce point.)
Faut-il utiliser la
int
surcharge ou lashort
surcharge? Nous ne savons tout simplement pas, nous devrons le spécifier avec votre[int] method1(num)
syntaxe. Ce qui est un peu pénible à analyser et à écrire pour être honnête.Le fait est que sa syntaxe est étonnamment similaire à une méthode générique en C #.
(C ++ et Java ont des fonctionnalités similaires.)
En bref, les concepteurs de langage ont choisi de résoudre le problème d'une manière différente afin de simplifier l'analyse et d'activer des fonctionnalités de langage beaucoup plus puissantes.
Vous dites que vous ne savez pas grand chose sur les compilateurs. Je recommande fortement d'apprendre les grammaires et les analyseurs. Une fois que vous comprendrez ce qu'est une grammaire sans contexte, vous aurez une bien meilleure idée de la raison pour laquelle l'ambiguïté est une mauvaise chose.
la source
short
etint
.method
renvoyait un short ou un int, et le type était défini comme long.long
/int
/short
concerne davantage la complexité du sous-typage et / ou des conversions implicites que la surcharge de type de retour. Après tout, les littéraux numériques sont surchargés sur leur type de retour en C ++, Java, C♯ et bien d'autres, et cela ne semble pas poser de problème. Vous pouvez simplement créer une règle: par exemple, choisissez le type le plus spécifique / général.Toutes les fonctionnalités linguistiques ajoutent de la complexité, elles doivent donc fournir suffisamment d'avantages pour justifier les pièges inévitables, les cas d'angle et la confusion des utilisateurs que chaque fonctionnalité crée. Pour la plupart des langues, celle-ci ne fournit tout simplement pas suffisamment d'avantages pour la justifier.
Dans la plupart des langues, vous vous attendriez à ce que l'expression
method1(2)
ait un type défini et une valeur de retour plus ou moins prévisible. Mais si vous autorisez une surcharge sur les valeurs de retour, cela signifie qu'il est impossible de dire ce que cette expression signifie en général sans tenir compte du contexte qui l'entoure. Considérez ce qui se passe lorsque vous avez uneunsigned long long foo()
méthode dont la mise en œuvre se termine parreturn method1(2)
? Cela devrait-il appeler lalong
surcharge -returning ou laint
surcharge -returning ou simplement donner une erreur de compilation?De plus, si vous devez aider le compilateur en annotant le type de retour, non seulement vous inventez plus de syntaxe (ce qui augmente tous les coûts susmentionnés pour permettre à la fonctionnalité d'exister), mais vous faites en fait la même chose que la création deux méthodes nommées différemment dans un langage "normal". Est
[long] method1(2)
plus intuitif quelong_method1(2)
?D'un autre côté, certains langages fonctionnels comme Haskell avec des systèmes de types statiques très puissants autorisent ce type de comportement, car leur inférence de type est suffisamment puissante pour que vous ayez rarement besoin d'annoter le type de retour dans ces langages. Mais cela n'est possible que parce que ces langages renforcent vraiment la sécurité des types, plus que tout langage traditionnel, tout en exigeant que toutes les fonctions soient pures et référentiellement transparentes. Ce n'est jamais quelque chose qui serait réalisable dans la plupart des langues POO.
la source
Il est disponible dans Swift et fonctionne très bien là-bas. De toute évidence, vous ne pouvez pas avoir un type ambigu des deux côtés, il doit donc être connu à gauche.
Je l'ai utilisé dans une simple API d'encodage / décodage .
Cela signifie que les appels où les types de paramètres sont connus, comme ceux
init
d'un objet, fonctionnent très simplement:Si vous portez une attention particulière, vous remarquerez l'
dechOpt
appel ci-dessus. J'ai découvert à la dure que la surcharge du même nom de fonction où le différenciateur renvoyait une option était trop sujette à erreur car le contexte d'appel pouvait introduire une attente, c'était une option.la source
la source
var1
et procéder à l'inférence de type et dès qu'une autre expression détermine le type d'var1
utilisation que pour sélectionner le bonne mise en œuvre. En fin de compte, montrer un cas où l'inférence de type n'est pas triviale prouve rarement un point autre que cette inférence de type est généralement non triviale.method1
doit être déclaré pour renvoyer un type particulier.