Warum kann der Entwickler bei dynamisch getippten Sprachen den Typ nicht angeben?

14

Die dynamisch getippten Sprachen, die ich kenne, lassen die Entwickler niemals die Variablentypen spezifizieren oder haben zumindest eine sehr eingeschränkte Unterstützung dafür.

JavaScript bietet beispielsweise keinen Mechanismus zum Erzwingen von Variablentypen, wenn dies zweckmäßig ist. PHP können Sie einige Arten von Methodenargumenten angeben, aber es gibt keine Möglichkeit , einheimische Arten zu verwenden ( int, stringusw.) für die Argumente, und es gibt keine Möglichkeit , Typen für irgendetwas zu erzwingen andere als Argumente.

Gleichzeitig wäre es zweckmäßig, in einigen Fällen den Typ einer Variablen in einer dynamisch typisierten Sprache anzugeben, anstatt die Typprüfung manuell durchzuführen.

Warum gibt es eine solche Einschränkung? Ist es aus technischen / Performance-Gründen (ich nehme an, es ist im Fall von JavaScript) oder nur aus politischen Gründen (was, glaube ich, der Fall von PHP ist)? Ist dies ein Fall für andere dynamisch typisierte Sprachen, mit denen ich nicht vertraut bin?


Bearbeiten: Nach den Antworten und Kommentaren hier ein Beispiel zur Verdeutlichung: Nehmen wir an, wir haben die folgende Methode in normalem PHP:

public function CreateProduct($name, $description, $price, $quantity)
{
    // Check the arguments.
    if (!is_string($name)) throw new Exception('The name argument is expected to be a string.');
    if (!is_string($description)) throw new Exception('The description argument is expected to be a string.');
    if (!is_float($price) || is_double($price)) throw new Exception('The price argument is expected to be a float or a double.');
    if (!is_int($quantity)) throw new Exception('The quantity argument is expected to be an integer.');

    if (!$name) throw new Exception('The name argument cannot be an empty string.');
    if ($price <= 0) throw new Exception('The price argument cannot be less or equal to zero.');
    if ($price < 0) throw new Exception('The price argument cannot be less than zero.');

    // We can finally begin to write the actual code.
    // TODO: Implement the method here.
}

Mit einigem Aufwand kann dies umgeschrieben werden als (siehe auch Programmierung durch Verträge in PHP ):

public function CreateProduct($name, $description, $price, $quantity)
{
    Component::CheckArguments(__FILE__, __LINE__, array(
        'name' => array('value' => $name, 'type' => VTYPE_STRING),
        'description' => array('value' => $description, 'type' => VTYPE_STRING),
        'price' => array('value' => $price, 'type' => VTYPE_FLOAT_OR_DOUBLE),
        'quantity' => array('value' => $quantity, 'type' => VTYPE_INT)
    ));

    if (!$name) throw new Exception('The name argument cannot be an empty string.');
    if ($price <= 0) throw new Exception('The price argument cannot be less or equal to zero.');
    if ($price < 0) throw new Exception('The price argument cannot be less than zero.');

    // We can finally begin to write the actual code.
    // TODO: Implement the method here.
}

Die gleiche Methode würde jedoch wie folgt geschrieben, wenn PHP optional native Typen für Argumente akzeptieren würde:

public function CreateProduct(string $name, string $description, double $price, int $quantity)
{
    // Check the arguments.
    if (!$name) throw new Exception('The name argument cannot be an empty string.');
    if ($price <= 0) throw new Exception('The price argument cannot be less or equal to zero.');
    if ($price < 0) throw new Exception('The price argument cannot be less than zero.');

    // We can finally begin to write the actual code.
    // TODO: Implement the method here.
}

Welches ist kürzer zu schreiben? Welches ist leichter zu lesen?

Arseni Mourzenko
quelle
1
Sie können optional Typen in einigen dynamisch typisierten Sprachen angeben, z. B. in Common Lisp.
SK-logic
Nicht wenige dynamisch typisierte Sprachen verwenden
Besetzungen
Einige tun. Objective-C ist beispielsweise dynamisch typisiert, Sie können jedoch einen Typ für Variablen deklarieren, und der Compiler gibt Warnungen aus, wenn Sie den erwarteten Typ nicht erhalten.
mipadi
1
Clojure ist ein Beispiel für eine Sprache, die normalerweise dynamisch typisiert wird. Sie können Variablentypen jedoch optional über "
Typhinweise" angeben
1
Groovy ist ein weiteres Beispiel für eine dynamisch typisierte Sprache, mit der ein Typ angegeben werden kann.
Eric Wilson

Antworten:

17

Der Sinn der statischen Typisierung ist die Fähigkeit , statisch zu beweisen, dass Ihr Programm in Bezug auf Typen korrekt ist (Hinweis: Nicht in allen Sinnen vollständig korrekt). Wenn Sie durchgehend über ein statisches Typsystem verfügen, können Sie die meiste Zeit Tippfehler erkennen.

Wenn Sie nur teilweise Typinformationen haben, können Sie nur die kleinen Teile eines Aufrufdiagramms überprüfen, in denen die Typinformationen zufällig vollständig sind. Sie haben jedoch viel Zeit und Mühe aufgewendet, um Typinformationen für unvollständige Teile anzugeben, die Ihnen nicht weiterhelfen können, aber ein falsches Sicherheitsgefühl vermitteln können.

Um Typinformationen auszudrücken, benötigen Sie einen Teil der Sprache, der nicht allzu einfach sein kann. Bald werden Sie feststellen, dass Informationen wie intnicht genug sind; Sie möchten so etwas wie List<Pair<Int, String>>dann parametrische Typen usw. Es kann verwirrend genug sein, auch im eher einfachen Fall von Java.

Dann müssen Sie diese Informationen während der Übersetzungs- und Ausführungsphase verarbeiten, da es dumm ist, nur nach statischen Fehlern zu suchen. Der Benutzer wird erwarten, dass die Typeinschränkungen immer gelten, wenn sie überhaupt angegeben werden. Dynamische Sprachen sind nicht zu schnell, und solche Überprüfungen verlangsamen die Leistung noch mehr. Eine statische Sprache kann ernsthafte Anstrengungen unternehmen, um Typen zu überprüfen, da dies nur einmal durchgeführt wird. eine dynamische Sprache kann nicht.

Stellen Sie sich nun vor, Sie fügen all dies hinzu und warten es, damit Benutzer diese Funktionen manchmal optional verwenden und nur einen kleinen Bruchteil der Tippfehler erkennen. Ich denke nicht, dass es die Mühe wert ist.

Der springende Punkt dynamischer Sprachen ist ein sehr kleiner und sehr formbarer Rahmen, in dem Sie auf einfache Weise Dinge tun können, die in einer statischen Sprache viel komplizierter sind: verschiedene Formen von Affen-Patching, die zum Metaprogrammieren, Verspotten und Testen, dynamisches Ersetzen von Code usw. Smalltalk und Lisp, beide sehr dynamisch, haben es so weit gebracht, Umgebungsbilder zu versenden, anstatt sie aus dem Quellcode zu erstellen. Wenn Sie jedoch sicherstellen möchten, dass bestimmte Datenpfade typsicher sind, fügen Sie Zusicherungen hinzu und schreiben Sie weitere Komponententests.

9000
quelle
1
+1, obwohl Tests nur zeigen können, dass in bestimmten Situationen keine Fehler auftreten. Sie sind ein schlechter Ersatz für den Beweis, dass (Typ-) Fehler unmöglich sind.
Ingo
1
@Ingo: sicher. Dynamische Sprachen eignen sich jedoch hervorragend zum Basteln und schnellen Erstellen von Prototypen, bei denen Sie relativ einfache Ideen sehr schnell zum Ausdruck bringen. Wenn Sie kugelsicheren Produktionscode wünschen, können Sie sich später einer statischen Sprache zuwenden, wenn Sie einige stabile Kernkomponenten extrahiert haben.
9000
1
@ 9000, ich bezweifle nicht, dass sie großartig sind. Ich wollte nur darauf hinweisen, dass das Schreiben von 3 oder 4 Lame-Tests keine Garantie für die Sicherheit des Typs ist .
Ingo
2
@ 9000, stimmt, und die schlechte Nachricht ist, dass es selbst dann praktisch unmöglich ist. Selbst Haskell- oder Agda-Code stützt sich auf Annahmen, wie zum Beispiel, dass die in der Laufzeit verwendete Bibliothek korrekt ist. Davon abgesehen ist es in einem Projekt mit etwa 1000 LOC, das sich über ein Dutzend Quellcodedateien erstreckt, so cool, wenn Sie etwas ändern können, und Sie wissen, dass der Compiler auf jede Zeile verweist, auf die sich die Änderung auswirkt.
Ingo
4
Schlecht geschriebene Prüfungen ersetzen nicht die statische Typprüfung: Sie sind minderwertig. Gut geschriebene Prüfungen sind auch kein Ersatz für die statische Typprüfung: Sie sind überlegen.
Rein Henrichs
8

In den meisten dynamischen Sprachen können Sie den Typ eines Objekts oder Werts zumindest dynamisch testen .

Für einige dynamische Sprachen gibt es statische Inferencer, Checker und / oder Enforcer: z

Und Perl 6 unterstützt ein optionales Typsystem mit statischer Typisierung.


Aber ich vermute, das Fazit ist, dass viele Leute dynamisch Sprachen benutzen, weil sie dynamisch getippt sind, und für sie ist die optionale statische Eingabe sehr "ho hum". Und viele andere Leute benutzen sie, weil sie "für Nicht-Programmierer leicht zu bedienen" sind, hauptsächlich als Folge der fehlerverzeihenden dynamischen Typisierung. Für sie ist die optionale Eingabe etwas, das sie entweder nicht verstehen oder nicht verwenden möchten.

Wenn Sie zynisch wären, könnten Sie sagen, dass die optionale statische Eingabe das Schlimmste aus beiden Welten bietet. Für einen Zealot mit statischem Typ werden nicht alle dynamischen Typfehler verhindert. Für einen dynamischen Fan ist es immer noch eine gerade Jacke ... obwohl die Träger nicht straff gespannt sind.

Stephen C
quelle
2
Es sollte beachtet werden, dass das Prüfen von Typen von den meisten Communities unter den meisten Umständen verpönt wird. Verwenden Sie beim Umgang mit Objekthierarchien den Polymorphismus (insbesondere "Duck Typing") und zwingen Sie sich, wenn möglich / sinnvoll, zum erwarteten Typ. In einigen wenigen Fällen ist es einfach nicht sinnvoll, einen Typ zuzulassen. In vielen Sprachen gibt es jedoch in den meisten Fällen eine Ausnahme. Daher ist eine Typüberprüfung nur selten sinnvoll.
4
"Leute benutzen normalerweise dynamisch Sprachen, weil sie dynamisch getippt sind" : JavaScript wird verwendet, weil es die einzige Sprache ist, die von den meisten Browsern unterstützt wird. PHP wird verwendet, weil es beliebt ist.
Arseni Mourzenko
2

Javascript hat geplant, einige optionale statische Typisierungen einzuschließen, und es scheint, als würden viele ausgereifte dynamische Sprachen in diese Richtung gehen.

Der Grund dafür ist, dass Sie beim ersten Codieren schnell und dynamisch tippen möchten. Sobald Ihr Code solide ist, funktioniert und viele Verwendungszwecke hat, möchten Sie das Design sperren, um Fehler zu reduzieren. (Dies ist sowohl für Benutzer als auch für Entwickler von Vorteil, da erstere bei ihren Aufrufen eine Fehlerprüfung erhalten und letztere nicht versehentlich Fehler verursachen.

Das ergibt für mich einen Sinn, da ich zu Beginn eines Projekts in der Regel zu viel Typprüfung finde, zu wenig am Ende der Laufzeit, egal welche Sprache ich benutze;).

Macke
quelle
Ich kenne diese Pläne nicht, die optionale statische Eingabe in JavaScript einzuschließen. aber ich hoffe, sie waren nicht so grausam wie in ActiveScript. das Schlimmste von JavaScript und Java.
Javier
Es war für JS 4 (oder ECMAscript 4) geplant, aber diese Version wurde aufgrund von Kontroversen fallen gelassen. Ich bin sicher, dass in Zukunft etwas Ähnliches in irgendeiner Sprache erscheinen wird. (In Python kann man das übrigens mit Dekorateuren machen.)
Macke
1
Dekorateure fügen eine dynamische Typprüfung hinzu, eine Art von Behauptungen. Aufgrund der extremen Dynamik der Sprache können Sie in Python keine umfassende statische Typprüfung durchführen, wie sehr Sie sich auch bemühen.
9000
@ 9000: Das ist richtig. Ich denke jedoch nicht, dass die dynamische Typprüfung schlecht ist (aber ich würde Enten-Typprüfvergleiche unter JS4 vorziehen), insbesondere in Kombination mit Unit-Tests, und die Typisierung von Dekorateuren könnte die Unterstützung von IDEs / Flusenprüfern verbessern, wenn sie vorhanden sind standardisiert.
Macke
natürlich ist es nicht schlecht! Dies ist keine Frage der Moral. Irgendwann muss der Typ auf die eine oder andere Weise "überprüft" werden. Wenn Sie * ((double *) 0x98765E) in C schreiben, wird die CPU dies tun und prüfen, ob 0x98765E tatsächlich ein Zeiger auf ein Double ist.
Ingo
2

Python - Objekte machen eine Art haben.

Sie geben den Typ an, wenn Sie das Objekt erstellen.

Gleichzeitig wäre es zweckmäßig, in einigen Fällen den Typ einer Variablen in einer dynamisch typisierten Sprache anzugeben, anstatt die Typprüfung manuell durchzuführen.

Eigentlich ist eine manuelle Typprüfung in Python fast immer eine Verschwendung von Zeit und Code.

Es ist einfach eine schlechte Praxis, in Python Code für die Typprüfung zu schreiben.

Wenn ein unangemessener Typ von einem böswilligen Soziopathen verwendet wurde, lösen die gewöhnlichen Methoden von Python eine gewöhnliche Ausnahme aus, wenn der Typ nicht geeignet ist.

Sie schreiben keinen Code, Ihr Programm schlägt immer noch mit einem fehl TypeError.

Es gibt sehr seltene Fälle, in denen Sie den Typ zur Laufzeit bestimmen müssen.

Warum gibt es eine solche Einschränkung?

Da es sich nicht um eine "Einschränkung" handelt, ist die Frage keine echte Frage.

S.Lott
quelle
2
"Python-Objekte haben einen Typ." - Ohh wirklich? Genau wie Perl-Objekte, PHP-Objekte und alle anderen Datenelemente der Welt. Der Unterschied zwischen statischer und dynamischer Typisierung besteht nur dann, wenn der Typ überprüft werden soll, dh wenn sich Typfehler manifestieren. Wenn sie als Compilerfehler angezeigt werden, handelt es sich um eine statische Typisierung. Wenn sie als Laufzeitfehler angezeigt werden, ist sie dynamisch.
Ingo
@Ingo: Danke für die Klarstellung. Das Problem ist, dass C ++ - und Java-Objekte von einem Typ in einen anderen umgewandelt werden können, was den Typ eines Objekts etwas trübe macht und daher die "Typprüfung" in diesen Compilern ebenfalls etwas trübe macht. Wo Python-Typprüfung - auch wenn es zur Laufzeit ist - ist viel weniger trübe. Außerdem kommt die Frage dem Sprichwort nahe, dass dynamisch getippte Sprachen keine Typen haben. Die gute Nachricht ist, dass dieser Fehler nicht häufig vorkommt.
S.Lott
1
Sie haben Recht, Typumwandlungen (im Gegensatz zu Typumwandlungen, dh ((double) 42)) unterwandern die statische Typisierung. Sie werden benötigt, wenn das Typsystem nicht leistungsfähig genug ist. Vor Java 5 gab es in Java keine parmeterisierten Typen. Ohne Typumwandlungen konnte man damals nicht leben. Heute ist es viel besser, aber dem Typensystem fehlen immer noch höherwertige Typen, ganz zu schweigen von höherrangigem Polymorphismus. Ich denke, es ist gut möglich, dass dynamisch getippte Sprachen genau deshalb so viele Anhänger haben, weil sie eines von zu engen Typsystemen befreien.
Ingo
2

Meistens müssen Sie dies nicht tun, zumindest nicht in dem Detailgrad, den Sie vorschlagen. In PHP machen die Operatoren, die Sie verwenden, klar, was Sie von den Argumenten erwarten. Es ist jedoch ein bisschen konstruktiv, dass PHP Ihre Werte umwandelt, wenn dies überhaupt möglich ist, selbst wenn Sie ein Array an eine Operation übergeben, die eine Zeichenfolge erwartet. und das ist genau das, wo Typprüfungen sind nützlich). Ansonsten spielt es keine Rolle, ob Sie Ganzzahlen 1und / 5oder Zeichenfolgen hinzufügen "1"und "5"- die bloße Tatsache, dass Sie die verwenden+Der Operator signalisiert PHP, dass Sie die Argumente als Zahlen behandeln möchten, und PHP gehorcht. Eine interessante Situation ist, wenn Sie Abfrageergebnisse von MySQL erhalten: Viele numerische Werte werden einfach als Zeichenfolgen zurückgegeben, aber Sie werden es nicht bemerken, da PHP sie immer dann für Sie umsetzt, wenn Sie sie als Zahlen behandeln.

Python ist in Bezug auf seine Typen etwas strenger, aber im Gegensatz zu PHP hatte Python von Anfang an Ausnahmen und verwendet sie konsequent. Das Paradigma "leichter um Verzeihung als um Erlaubnis zu bitten" schlägt vor, die Operation nur ohne Typprüfung auszuführen und sich auf eine Ausnahme zu verlassen, die ausgelöst wird, wenn die Typen keinen Sinn ergeben. Der einzige Nachteil, an den ich denken kann, ist, dass Sie manchmal feststellen werden, dass ein Typ nicht mit dem übereinstimmt, was Sie erwarten, aber es kann mühsam sein, den Grund dafür zu finden.

Und es ist ein weiterer Grund zu berücksichtigen: Dynamische Sprachen nicht haben eine Zusammenstellung der Bühne. Selbst wenn Sie Typeinschränkungen haben, können diese nur zur Laufzeit ausgelöst werden, weil keine Kompilierungszeit vorhanden ist . Wenn Ihre Prüfungen trotzdem zu Laufzeitfehlern führen, ist es viel einfacher, sie entsprechend zu modellieren: Als explizite Prüfungen (wie is_XXX()in PHP oder typeofin Javascript) oder durch Ausnahmen (wie in Python). Funktionell haben Sie den gleichen Effekt (ein Fehler wird zur Laufzeit gemeldet, wenn eine Typprüfung fehlschlägt), aber er lässt sich besser in die übrige Semantik der Sprache integrieren. Es ist einfach nicht sinnvoll, Typfehler, die sich grundlegend von anderen Laufzeitfehlern unterscheiden, in einer dynamischen Sprache zu behandeln.

tdammers
quelle
0

Sie könnten an Haskell interessiert sein - das Typensystem leitet die Typen aus dem Code ab und Sie können auch Typen angeben.

daven11
quelle
5
Haskell ist eine großartige Sprache. Es ist irgendwie ein gegenüber dynamischen Sprachen: Sie viel Zeit beschreiben Typen verbringen, und in der Regel , wenn Sie Ihre Typen das Programm funktioniert :) dachte ich
9000
@ 9000: In der Tat. Sobald es kompiliert ist, funktioniert es normalerweise. :)
Macke
@Macke - für verschiedene Werte normalerweise natürlich. :-) Für mich ist der größte Vorteil des Typsystems und des Funktionsparadigmas, wie ich an anderer Stelle betont habe, dass es nicht wichtig ist, ob sich eine Änderung stillschweigend auf irgendeinen abhängigen Code anderswo auswirkt - der Compiler wird auf Typfehler hinweisen und veränderlicher Zustand existiert einfach nicht.
Ingo
0

Wie die anderen Antworten bereits angedeutet haben, gibt es beim Implementieren einer Programmiersprache zwei Ansätze für die Eingabe.

  1. Lassen Sie sich vom Programmierer mitteilen, welche Variablen und Funktionen für Typen verwendet werden. Im Idealfall überprüfen Sie auch, ob die Typspezifikationen korrekt sind. Dann können Sie, da Sie wissen, welche Art von Dingen an jedem Ort vorhanden sein wird, Code schreiben, der davon ausgeht, dass die entsprechende Sache vorhanden ist, und die von Ihnen verwendete Datenstruktur verwenden, um diesen Typ direkt zu implementieren.
  2. Fügen Sie den Werten, die in Variablen gespeichert und an Funktionen übergeben und von diesen zurückgegeben werden, einen Typindikator hinzu. Dies bedeutet, dass der Programmierer keine Typen für Variablen oder Funktionen angeben muss, da die Typen tatsächlich zu den Objekten gehören, auf die sich die Variablen und Funktionen beziehen.

Beide Ansätze sind gültig und deren Verwendung hängt zum Teil von technischen Überlegungen wie der Leistung und zum Teil von politischen Gründen wie dem Zielmarkt für die Sprache ab.

Larry Coleman
quelle
0

Zuallererst sind dynamische Sprachen hauptsächlich für die Benutzerfreundlichkeit geschaffen worden. Wie Sie bereits erwähnt haben, ist es sehr gut, die Typkonvertierung automatisch durchzuführen und uns weniger Overhead zu bieten. Gleichzeitig mangelt es aber an Performance-Problemen.

Sie können bei den dynamischen Sprachen bleiben, wenn Sie sich nicht um die Leistung sorgen. Angenommen, JavaScript wird langsamer ausgeführt, wenn in Ihrem Programm viele Typkonvertierungen durchgeführt werden müssen, dies hilft jedoch, die Anzahl der Zeilen in Ihrem Code zu verringern.

Zu erwähnen ist, dass es auch andere dynamische Sprachen gibt, mit denen der Programmierer den Typ angeben kann. Beispielsweise ist Groovy eine der bekanntesten dynamischen Sprachen, die unter JVM ausgeführt werden. Und es ist sogar in den letzten Tagen sehr berühmt gewesen. Beachten Sie, dass die Leistung von Groovy mit der von Java identisch ist.

Hoffe es hilft dir.

Ameisen
quelle
-1

Es macht einfach keinen Sinn, dies zu tun.

Warum?

Da das Typsystem von DTLs genau so ist, dass Typen zur Kompilierungszeit nicht bestimmt werden können. Daher konnte der Compiler nicht einmal prüfen, ob der angegebene Typ sinnvoll ist.

Ingo
quelle
1
Warum? Es ist durchaus sinnvoll, einen Compiler darauf hinzuweisen, welche Typen zu erwarten sind. Es wird keiner der Einschränkungen des Typsystems widersprechen.
SK-logic
1
SK-Logik: Wenn dynamisch typisiert bedeutet, dass jede Funktion / Methode / Operation Objekte vom Typ "Dynamisch", "Beliebig" oder was auch immer annimmt und "Dynamisch", "Beliebig" oder was auch immer zurückgibt, gibt es im Allgemeinen keine Möglichkeit, diesen bestimmten Wert zu bestimmen wird zum Beispiel immer eine ganze Zahl sein. Daher muss der Laufzeitcode sowieso nach Nicht-Ganzzahlen suchen, so als ob der Typ an erster Stelle "Dynamisch" wäre. Dies ist genau das, was ein statisches Typsystem tut: Es ermöglicht den Nachweis, dass eine bestimmte Variable, ein bestimmtes Feld oder eine bestimmte Methode immer einen bestimmten Typ zurückgibt .
Ingo
@Ingo, nein, es gibt einen Weg. Sehen Sie sich zum Beispiel an, wie es in Common Lisp implementiert ist. Dies ist besonders nützlich für lokale Variablen - Sie können die Leistung drastisch steigern, indem Sie alle Tipptipps einfügen.
SK-logic
@ SK-logic: Kannst du mir vielleicht sagen, wann und wie Schreibfehler in CL erkannt werden? Wie auch immer, @MainMa hat den Status Quo in seiner Frage ganz gut zusammengefasst: Es ist genau das, was man von "rein" dynamischen Sprachen erwarten kann.
Ingo
@Ingo, warum denkst du, dass Typen nur zum statischen Nachweis der Korrektheit nützlich sind? Dies gilt nicht einmal für Sprachen wie C, in denen Sie ein ungeprüftes Typ-Casting haben. Typanmerkungen in dynamischen Sprachen sind hauptsächlich als Compilerhinweise nützlich, die die Leistung verbessern oder eine konkrete numerische Darstellung angeben. Ich würde zustimmen, dass in den meisten Fällen Anmerkungen die Semantik des Codes nicht ändern sollten.
SK-logic
-1

Schauen Sie sich Go an, auf der Oberfläche ist es statisch typisiert, aber diese Typen können Schnittstellen sein, die im Wesentlichen dynamisch sind.

dan_waterworth
quelle