Ist es möglich, eine dynamisch getippte Sprache zu haben, ohne Enten zu tippen? [geschlossen]

9

Diese Frage wurde hier gestellt , erhielt jedoch schlechte Antworten und hat das Problem nicht geklärt. Ich glaube, es rechtfertigt es, es noch einmal zu fragen.

Ich verstehe, dass Sie Enten entweder mit dynamisch oder mit statisch typisierten Sprachen tippen können (Beispiele dafür sind jedoch selten, wie z. B. die Vorlagen von C ++).

Ich bin mir jedoch nicht sicher, ob es eine dynamisch getippte Sprache ohne Ententipp gibt.

Ententypisierung bedeutet, dass der Typ eines Objekts auf den Operationen und Attributen basiert, die es zu einem bestimmten Zeitpunkt hat. Gibt es eine Möglichkeit, dynamisches Tippen durchzuführen, ohne zwangsläufig das Enten-Tippen zu unterstützen?

Schauen wir uns zum Beispiel diesen Python-Code an:

def func(some_object)
    some_object.doSomething()

something = Toaster()
func(something)

In dynamisch typisierten Sprachen ist der Typ eines Objekts nur zur Laufzeit bekannt. Wenn Sie also versuchen, eine Operation darauf auszuführen (z. B. some_object.doSomething()), hat die Laufzeit nur eine Wahl : Sie muss überprüfen, ob die Art der some_objectStützen doSomething()genau die Ententypisierung ist oder nicht .

Ist es also möglich, dynamisch typisierte Sprachen zu haben, ohne Enten zu tippen? Bitte erkläre.

Aviv Cohn
quelle
1
Ich habe ein paar Sprachen (Javascript, Ruby und PHP) auf Wikipedia überprüft und letzteres soll dynamisch und schwach sein, nicht vom Typ Ente. Das könnte Ihre Frage beantworten.
Trylks
Tatsächlich hat die Laufzeit die Wahl, die Typensignatur des Objekts zu überprüfen, bevor versucht wird, auf eines seiner Felder zuzugreifen oder seine Methoden aufzurufen. Das wäre "starke dynamische Typisierung". Beachten Sie, dass Python beispielsweise teilweise auf diese Weise funktioniert - beispielsweise wird beim Versuch explizit ein Typfehler ausgegeben 1 + "1". In Pythons Fall fehlt die Disziplin der Überprüfung so gut wie und es liegt an der Implementierung des Benutzercodes, die Typen zu überprüfen, ob der Benutzer (im Gegensatz zur Python-Laufzeit) dies nützlich findet. Beachten Sie auch, dass die Typisierung von Enten im Vergleich zur Typisierung von Nicht-Enten der nominellen vs. strukturellen Typisierung entspricht (siehe Wikipedia).
Michael Pankov
Vielleicht sollten Sie eine Gans anstelle einer Ente verwenden, um für Sie zu tippen?
Jwenting

Antworten:

19

Um sicherzustellen, dass wir über die gleichen Dinge sprechen, würde ich zunächst mit einigen Definitionen beginnen.

Statische Typisierung bedeutet, dass Typfehler zur Kompilierungszeit gemeldet werden, während dynamische Typisierung bedeutet, dass Typfehler zur Laufzeit gemeldet werden.

Ententypisierung bedeutet, dass ein Teil des Codes erfordert, dass ein Objekt die verwendeten Operationen unterstützt und nicht mehr.

Die strukturelle Typisierung erfordert, dass ein Objekt einen bestimmten Satz von Operationen unterstützt (auch wenn einige von ihnen möglicherweise nicht verwendet werden).

Für die nominelle Typisierung muss das Objekt genau vom angegebenen Typ oder ein Untertyp dieses Typs sein.

Wie Sie sehen können, ist die strukturelle Typisierung strenger als die Ententypisierung, und die nominelle Typisierung ist strenger als die strukturelle.

Jetzt werde ich über die TypeScript-Sprache sprechen , da sie die meisten dieser Optionen gut veranschaulicht.

Betrachten Sie das folgende TypeScript-Programm:

interface Person {
    Name : string;
    Age : number;
}

function greet(person : Person) {
    alert("Hello, " + person.Name);
}

greet({ Name: "svick" });

Da das übergebene Objekt greetnicht über die AgeEigenschaft verfügt, verursacht dies einen Fehler bei der Kompilierung, der zeigt, dass TypeScript statische strukturelle Typisierung verwendet .

Trotz des Fehlers wird der obige Code tatsächlich mit dem folgenden JavaScript kompiliert, das einwandfrei funktioniert:

function greet(person) {
    alert("Hello, " + person.Name);
}

greet({ Name: "svick" });

Dies zeigt, dass TypeScript auch die dynamische Ententypisierung verwendet .

Wenn der Code stattdessen wie folgt kompiliert wurde:

function greet(person) {
    if (!(typeof(person.Name) == 'string' && typeof(person.Age) == 'number'))
        throw 'TypeError';

    alert("Hello, " + person.Name);
}

Dies wäre dann ein Beispiel für eine dynamische strukturelle Typisierung , da überprüft wird, ob das Objekt die erforderlichen Eigenschaften der erforderlichen Typen aufweist, auch wenn die Funktion selbst diese nicht benötigt.

Wenn es kompiliert wurde zu:

function greet(person) {
    if (!(person instanceof Person))
        throw 'TypeError'

    alert("Hello, " + person.Name);
}

Dies wäre ein Beispiel für eine dynamische nominelle Typisierung , da der Name des Objekttyps und nicht dessen Struktur überprüft wird.

Dies alles zeigt, dass eine dynamische Typisierung ohne Ente möglich ist (sowohl strukturell als auch nominal). Dieser Ansatz wird jedoch nicht sehr häufig verwendet, da er hauptsächlich die Nachteile der Nicht-Enten-Typisierung (Sie müssen Typen explizit angeben; weniger flexibel) und der dynamischen Typisierung (Typfehler werden nur zur Laufzeit und nur im tatsächlich ausgeführten Code angezeigt) kombiniert ).

Wenn Sie Typanmerkungen hinzufügen möchten, um die Eingabe ohne Enten zu ermöglichen, können Sie die Typen auch zur Kompilierungszeit überprüfen.

svick
quelle
1
Ich würde nicht sagen, dass Typoskript die dynamische Ententypisierung verwendet. Die Tatsache, dass tsc trotz Kompilierungsfehlern immer noch Javascript erzeugt, klingt für mich seltsam, aber trotzdem ... wurde der Tippfehler zur Kompilierungszeit gemeldet, und der Laufzeitteil bleibt Javascript und nicht Typoskript überlassen. Typoskript verwendet jedoch auch die statische Ententypisierung. Wenn Sie "var t = 1;" schreiben, wird angenommen, dass die Variable eine Zahl ist.
Joel
@Joel Ja, TypeScript erbt die dynamische Ententypisierung von JavaScript, aber das macht es nicht weniger zu einer Eigenschaft von TypeScript. Und Ihr Beispiel zeigt Typinferenz, nicht Ententypisierung.
Svick
Wie würden Sie eine Sprache nennen, in der Methoden als Teile von Schnittstellen definiert werden müssen, die Speicherorte jedoch mit beliebigen Schnittstellenkombinationen eingegeben werden können? Wenn Übergang [3,4]zu einem gewissen Verfahren einer Sammlung Halte [1,2]Ausbeuten [1,2,3,4]und vorbei [3,4]an einige ein Verfahren zur Herstellung einiger Sammlung Halte [1,2]würde [4,6], sollten die Methoden den gleichen Namen haben? Den ersten Sequence$Appendable$Addund den zweiten NumericVector$AddSequenceNumericVector
aufrufen
... scheinen sauberer zu sein als nur zu hoffen, dass homografische Namen keine Verwirrung stiften. In Sprachen, in denen jede Variable nur ein Typ sein muss, könnte ein "rein dynamischer" Typ hilfreich sein, aber wenn zusammengesetzte Typen verfügbar wären, würde ich denken, dass 99% der Anwendungsfälle für "out of the blue" duck eliminiert würden Tippen.
Supercat
Ich bin geneigt, @Joel zuzustimmen. Der Unterschied zwischen einer Warnung und einer Ausnahme hat wenig mit der Eingabe der Sprache zu tun. TypeScript ist eine Bibliothek. tscist eine Schnittstelle hinein. Wenn Sie die Bibliothek verwenden, wird ein Ereignis ausgelöst. Wenn nichts zuhört, erhalten Sie standardmäßig ein Skript. Wenn Sie zuhören und eine Ausnahme auslösen, können Sie verhindern, dass das Skript generiert wird. Ändert das das Typsystem von TypeScript? Natürlich nicht.
Evan Carroll
3

Anscheinend (nach dem, was ich gelesen habe) hat die Eingabe von Enten nur in einem objektorientierten Kontext eine Bedeutung, wenn Funktionen als Methoden an Objekte angehängt werden. Wenn Sie dann schreiben duck.quacks(3), funktioniert dies, wenn der aktuelle Wert von duckdie Methode hat quacks.

Die dynamische Typisierung ist nicht unbedingt mit Methoden an eine OO-Ansicht gebunden.

Sie können den Typ realmit dem zugeordneten Operator oder der zugeordneten Funktion +: real*real->realund den Typ rationalmit dem zugeordneten Operator definieren +: rational*rational->rational. Wenn Sie dann a+bin einem dynamisch überprüften Zeitsystem beide Variablen schreiben aund bmöglicherweise einen +Operator haben, wird jedoch ein Laufzeitfehler angezeigt .

Dynamische Typisierungsprüfungen auf kategoriale Konsistenz von Werten, möglicherweise mehrere davon.

Duck Typing überprüft die Verhaltenskonsistenz des Codes mit einem Objekt (soweit ich es verstehe).

In gewisser Weise ist die Ententypisierung eine Form des Laufzeitpolymorphismus, mit der Ausnahme, dass sie nur für Zugriffsmethoden eines einzelnen Objekts gilt.

Man könnte jedoch möglicherweise eine allgemeinere Form des Laufzeitpolymorphismus definieren, bei der der auszuführende Operator +auf der Grundlage aller Argumente bestimmt wird. Damit eine Ente und ein Huhn zusammen tanzen können, wenn sie eine gemeinsame Tanzfunktion haben. Sie könnten mehrere haben, damit Enten auch mit Gänsen mit einer anderen Funktion tanzen können. Das scheint aber etwas kompliziert. Soweit ich mich erinnere, war mit den generischen Funktionen der Sprache EL1 , einem sehr alten Vorläufer objektorientierter Sprachen, möglicherweise etwas Ähnliches (mit wahrscheinlich mehr Struktur) möglich .

babou
quelle
Python unterstützt auch das Überladen von Operatoren. def add (a, b): return a + b ist machbar. Es wird automatisch in ein .__ add __ (b) konvertiert. ... Also ja, das Tippen von Enten scheint eine Art fortgeschrittener benutzerdefinierter Typen zu erfordern. Obwohl Datenelemente auch funktionieren, wären auch Entenstrukturen möglich, ähnlich wie folgt: def addstuff (a, b, t): t.stuff = a.stuff + b.stuff
StarWeaver
@ StarWeaver Na ja. Was mich an Ihrem Beispiel jedoch stört, ist die Assymetrie zwischen den Operanden. Wenn ein Typfehler vorliegt, liegt dies entweder daran, dass a nicht über die richtige Methode verfügt, oder daran, dass zwischen den Methoden von a und dem Typ von b eine Typinkongruenz besteht. Je nach Situation erhalten Sie also eine andere Ausnahme (wenn meine Erinnerung an Python korrekt ist). Ich möchte vielleicht eine Abstraktionsebene, in der ich nur eine Ausnahme bekomme, die besagt, dass a und b nicht zusammen + können (Entschuldigung für den seltsamen Satz). Aber das kann Geschmackssache sein. Ich möchte auch sagen, dass + kommutativ ist.
Babou
-2

Eine analoge Antwort:

Können Sie ein Cabrio kaufen und niemals das Verdeck ablegen? Sicher. Dies ist wahrscheinlich nicht der beste Weg, um Ihre Ressourcen auszugeben, da Sie für einige Funktionen (z. B. Cabrioverdeck, zusätzliche strukturelle Versteifung aufgrund fehlenden Daches als strukturelles Element) zusätzlich bezahlt werden und schlechtere Ergebnisse erzielen (z. B. zusätzliche Straßengeräusche, möglicherweise weniger) Crash-Sicherheit, kleinere Ablagefächer) als Folge der Investition in diese Funktion, die Sie nicht nutzen werden. Aber es ist technisch machbar.

Das Gleiche gilt für dynamische Sprachen und das Tippen von Enten. Sie haben die höhere Effizienz und die Sicherheitsgarantie für statische Sprachen zur Kompilierungszeit aufgegeben. Für was? Im Allgemeinen zur Vereinfachung der Ententypisierung. Variablen und Sammlungen können alles enthalten, und Sie müssen nicht viel im Voraus angeben, was genau. Gemischte Sammlungen wie [ 12, "gumbo", 12.4, 4+4j ](eine Ganzzahl, eine Zeichenfolge, ein Gleitkommawert und ein komplexer Wert) sind trivial, und Sie haben nicht die konstante Typumwandlung, die Sie beispielsweise in Java-Code sehen.

In einer dynamischen Sprache wie Python können Objekte erstellt werden, die nicht vom Typ Ente sind:

class Hungarian(object):
    self __init__(self):
        self.value = []
    self addInt(self, intValue):
        self.value.append(intValue)
    self addFloat(self, floatValue):
        self.value.append(floatValue)
    self addComplex(self, complexValue):
        self.value.append(complexValue)
    # ...

Wie Sie vielleicht bemerken, werden die Typen nicht wirklich überprüft, und jede der Methoden wird mit einer integrierten Struktur vom Typ Ente ( list) implementiert . Nachdem Sie den Preis für Dynamik bezahlt haben, können Sie auch das Verdeck ablegen und die daraus resultierende Einfachheit erhalten:

class NotHungarian(object):
    def __init__(self):
        self.value = []
    def add(self, whatever):
        self.value.append(whatever)
Jonathan Eunice
quelle
Inwiefern ist diese HungarianKlasse nicht vom Typ Ente? Wie Sie bereits betont haben, erfolgt keine Überprüfung der Typen.
Svick
@svick Es soll typspezifisch wie Java verwendet werden, wobei separate Methoden nicht von der durchgeführten Aktion, sondern auch von den verwendeten Typen abhängen. Die Eingabe von Enten würde unabhängig vom hinzugefügten Typ dieselbe Methode verwenden wie dies auch der NotHungarianFall ist. Die Eingabe von Enten hängt nicht nur davon ab, dass der Typ nicht überprüft wird, sondern auch, dass derselbe Aufruf- / Methoden- / Nachrichtenname ( add) verwendet wird. ( NotHungarianVerwendet auch einen Methodennamen add, der mit anderen Python-Objekten üblich ist, wie z. B. set"Quacksalber" wie diese anderen Objekte / Klassen.)
Jonathan Eunice
1
Diese Antwort ist etwas falsch in Bezug auf die Motivation, „die höhere Effizienz und die Sicherheitsgarantien für statische Sprachen zur Kompilierungszeit aufzugeben“. Die Motivation bestand darin, die Fesseln eines Fortran- oder C-ähnlichen Systems loszulassen, da diese Systeme häufig im Weg stehen (denken Sie an Pascals Zeichenfolgen mit fester Länge), generische Programmierung verhindern und wahnsinnig leistungsfähige Funktionen wie evalunmöglich machen. Die Motivation war nie, Typen loszuwerden, und einige dynamische Sprachen haben „allmähliches Tippen“. MJD hat eine schöne Präsentation über statische und dynamische Typisierung.
Amon
@amon nicht einverstanden. Statisch typisierte Sprachen haben im Allgemeinen einen ziemlich bedeutenden Vorteil gegenüber dynamisch typisierten Sprachen, da sie Unboxed / Value-Typen, Strukturen anstelle von Dicts usw. verwendeten. JIT-Compiler haben die Lücke deutlich verringert, aber statisch kompilierte AOT-Sprachen sind immer noch deutlich schneller. Das ist nicht der einzige Grund, eine Sprache zu wählen. Ich benutze Dynamic, wann immer ich kann, und die Leistungseinbußen sind die meiste Zeit begrenzt oder unwichtig. Und ja, es gibt andere Gründe für die Wahl der Dynamik. Aber Sie geben echte Dinge auf, um dorthin zu gelangen.
Jonathan Eunice