Ich kenne nicht alle Programmiersprachen, aber es ist klar, dass normalerweise die Möglichkeit einer Überladung einer Methode unter Berücksichtigung ihres Rückgabetyps (vorausgesetzt, ihre Argumente haben dieselbe Anzahl und denselben Typ) nicht unterstützt wird.
Ich meine so etwas:
int method1 (int num)
{
}
long method1 (int num)
{
}
Es ist nicht so, dass es ein großes Problem für die Programmierung ist, aber manchmal hätte ich es begrüßt.
Natürlich gibt es für diese Sprachen keine Möglichkeit, dies zu unterstützen, ohne zu unterscheiden, welche Methode aufgerufen wird, aber die Syntax dafür kann so einfach sein wie [int] method1 (num) oder [long] method1 (num) Auf diese Weise würde der Compiler wissen, welcher aufgerufen werden würde.
Ich weiß nicht, wie Compiler funktionieren, aber das scheint nicht so schwierig zu sein. Deshalb frage ich mich, warum so etwas normalerweise nicht implementiert wird.
Was sind die Gründe, warum so etwas nicht unterstützt wird?
quelle
Foo
undBar
.Antworten:
Dies erschwert die Typprüfung.
Wenn Sie nur eine Überladung basierend auf Argumenttypen zulassen und nur die Ableitung von Variablentypen aus ihren Initialisierern zulassen, fließen alle Ihre Typinformationen in eine Richtung: den Syntaxbaum hinauf.
Wenn Sie zulassen, dass Typinformationen in beide Richtungen übertragen werden, z. B. indem Sie den Typ einer Variablen aus ihrer Verwendung ableiten, benötigen Sie einen Einschränkungslöser (z. B. Algorithmus W für Hindley-Milner-Typsysteme), um den Typ zu bestimmen.
Hier mussten wir den Typ
x
als ungelöste Typvariable∃T
belassen, wobei wir nur wissen, dass er analysierbar ist. Erst später, wennx
es für einen konkreten Typ verwendet wird, verfügen wir über genügend Informationen, um die Einschränkung zu lösen und die zu bestimmen∃T = int
, die Typinformationen über den Syntaxbaum vom Aufrufausdruck in weitergebenx
.Wenn wir den Typ von nicht bestimmen könnten
x
, wäre entweder dieser Code ebenfalls überladen (so würde der Aufrufer den Typ bestimmen) oder wir müssten einen Fehler über die Mehrdeutigkeit melden.Daraus kann ein Sprachdesigner schließen:
Dies erhöht die Komplexität der Implementierung.
Dadurch wird die Typüberprüfung langsamer - in pathologischen Fällen exponentiell.
Es ist schwieriger, gute Fehlermeldungen zu erstellen.
Es ist zu verschieden vom Status Quo.
Ich habe keine Lust, es umzusetzen.
quelle
Weil es mehrdeutig ist. Am Beispiel von C #:
Welche Überlastung sollen wir verwenden?
Ok, vielleicht war das etwas unfair. Der Compiler kann nicht herausfinden, welcher Typ verwendet werden soll, wenn wir ihn nicht in Ihrer hypothetischen Sprache angeben. Implizites Tippen ist in Ihrer Sprache also unmöglich und es gibt anonyme Methoden und Linq dazu ...
Wie wäre es mit diesem? (Leicht neu definierte Signaturen, um den Punkt zu veranschaulichen.)
Sollten wir die
int
Überlastung oder dieshort
Überlastung verwenden? Wir wissen es einfach nicht, wir müssen es mit Ihrer[int] method1(num)
Syntax angeben . Um ehrlich zu sein, ist es ein bisschen mühsam, zu analysieren und zu schreiben .Die Syntax ähnelt überraschend einer generischen Methode in C #.
(C ++ und Java haben ähnliche Funktionen.)
Kurz gesagt, Sprachdesigner haben sich entschieden, das Problem auf eine andere Weise zu lösen, um das Parsen zu vereinfachen und viel leistungsfähigere Sprachfunktionen zu ermöglichen.
Sie sagen, Sie wissen nicht viel über Compiler. Ich würde wärmstens empfehlen, etwas über Grammatiken und Parser zu lernen. Sobald Sie verstanden haben, was eine kontextfreie Grammatik ist, haben Sie eine viel bessere Vorstellung davon, warum Mehrdeutigkeit eine schlechte Sache ist.
quelle
short
undint
.method
ein Short oder Int zurückgegeben würde und der Typ als Long definiert würde.long
/int
/short
geht es mehr um die Komplexität der Untertypisierung und / oder impliziten Konvertierungen als um die Überladung von Rückgabetypen. Schließlich sind Zahlenliterale bei ihrem Rückgabetyp in C ++, Java, C♯ und vielen anderen überladen, und das scheint kein Problem zu sein. Sie können sich einfach eine Regel ausdenken: z. B. den spezifischsten / allgemeinsten Typ auswählen.Alle Sprachfunktionen erhöhen die Komplexität, sodass sie genügend Nutzen bieten müssen, um die unvermeidlichen Fallstricke, Eckfälle und Benutzerverwirrungen zu rechtfertigen, die jede Funktion verursacht. Für die meisten Sprachen bietet diese Sprache einfach nicht genug Nutzen, um dies zu rechtfertigen.
In den meisten Sprachen würde man erwarten, dass der Ausdruck
method1(2)
einen bestimmten Typ und einen mehr oder weniger vorhersehbaren Rückgabewert hat. Wenn Sie jedoch eine Überladung der Rückgabewerte zulassen, ist es unmöglich zu sagen, was dieser Ausdruck im Allgemeinen bedeutet, ohne den Kontext zu berücksichtigen. Überlegen Sie, was passiert, wenn Sie eineunsigned long long foo()
Methode haben, deren Implementierung endetreturn method1(2)
? Sollte das dielong
-returning overload oder dieint
-returning overload aufrufen oder einfach einen Compilerfehler geben?Wenn Sie dem Compiler durch Annotieren des Rückgabetyps helfen müssen, erfinden Sie nicht nur mehr Syntax (was alle oben genannten Kosten für das Vorhandensein der Funktion erhöht), sondern Sie tun auch das Gleiche wie das Erstellen zwei unterschiedlich benannte Methoden in einer "normalen" Sprache. Ist
[long] method1(2)
intuitiver alslong_method1(2)
?Auf der anderen Seite erlauben einige funktionale Sprachen wie Haskell mit sehr starken statischen Typsystemen diese Art von Verhalten, da ihre Typinferenz stark genug ist, dass Sie den Rückgabetyp in diesen Sprachen selten mit Anmerkungen versehen müssen. Dies ist jedoch nur möglich, weil diese Sprachen die Typensicherheit mehr als jede herkömmliche Sprache wirklich erzwingen und alle Funktionen rein und referenziell transparent sein müssen. In den meisten OOP-Sprachen ist dies niemals möglich.
quelle
Es ist in Swift verfügbar und funktioniert dort einwandfrei. Natürlich kann man nicht auf beiden Seiten einen mehrdeutigen Typ haben, daher muss er auf der linken Seite bekannt sein.
Ich habe dies in einer einfachen Kodierungs- / Dekodierungs-API verwendet .
Das bedeutet, dass Aufrufe, bei denen Parametertypen bekannt sind, z. B.
init
eines Objekts, sehr einfach funktionieren:Wenn Sie genau hinschauen, werden Sie den
dechOpt
obigen Anruf bemerken . Ich fand heraus, dass das Überladen desselben Funktionsnamens, bei dem das Unterscheidungsmerkmal ein optionales Element zurückgab, zu fehleranfällig war, da der aufrufende Kontext die Erwartung hervorrufen konnte, dass es sich um ein optionales handelt.quelle
quelle
var1
und mit der Typinferenz fortfahren. Sobald ein anderer Ausdruck die Art dervar1
Verwendung für die Auswahl des bestimmt richtige Umsetzung. Unter dem Strich zeigt die Darstellung eines Falls, in dem die Typinferenz nicht trivial ist, selten, dass ein anderer Punkt als diese Typinferenz im Allgemeinen nicht trivial ist.method1
muss deklariert werden, um einen bestimmten Typ zurückzugeben.