Beim Kuriosieren auf der Hauptseite einer Website einer Skript-Programmiersprache bin ich auf folgende Passage gestoßen:
Wenn ein System zu groß wird, um im Kopf zu bleiben, können Sie statische Typen hinzufügen.
Dies erinnerte mich daran, dass in vielen Religionskriegen zwischen statischen, kompilierten Sprachen (wie Java) und dynamischen, interpretierten Sprachen (hauptsächlich Python, weil es häufiger verwendet wird, aber es ist ein "Problem", das von den meisten Skriptsprachen geteilt wird) eine der Beschwerden von statisch ist Fans typisierter Sprachen gegenüber dynamisch getippten Sprachen sind, dass sie sich nicht gut für größere Projekte skalieren lassen, weil "eines Tages Sie den Rückgabetyp einer Funktion vergessen und nachschlagen müssen, während bei statisch typisierten Sprachen alles vorhanden ist wird ausdrücklich deklariert ".
Ich habe solche Aussagen nie verstanden. Um ehrlich zu sein, selbst wenn Sie den Rückgabetyp einer Funktion deklarieren, können und werden Sie ihn vergessen, nachdem Sie viele Codezeilen geschrieben haben, und Sie müssen immer noch zu der Zeile zurückkehren, in der sie mit der Suchfunktion von deklariert wurde Ihr Texteditor, um es zu überprüfen.
Wenn Funktionen mit deklariert werden type funcname()...
, ohne zu wissen, dass type
Sie über jede Zeile suchen müssen, in der die Funktion aufgerufen wird, weil Sie nur wissen funcname
, während Sie in Python und dergleichen nur suchen def funcname
oder function funcname
was nur einmal vorkommt die Erklärung.
Darüber hinaus ist es bei REPLs trivial, eine Funktion auf ihren Rückgabetyp mit verschiedenen Eingaben zu testen, während Sie bei statisch typisierten Sprachen einige Codezeilen hinzufügen und alles neu kompilieren müssen, um den deklarierten Typ zu kennen.
Wie ist die statische Typisierung in größeren Projekten wirklich hilfreich, außer um den Rückgabetyp einer Funktion zu kennen, die eindeutig keine Stärke statisch typisierter Sprachen ist?
quelle
Antworten:
Es ist nicht trivial. Es ist nicht trivial überhaupt . Es ist nur trivial, dies für triviale Funktionen zu tun.
Sie können beispielsweise trivial eine Funktion definieren, bei der der Rückgabetyp vollständig vom Eingabetyp abhängt.
In diesem Fall
getAnswer
nicht wirklich hat einen einzigen Rückgabetyp. Es gibt keinen Test, den Sie jemals schreiben können, der dies mit einer Beispieleingabe aufruft, um den Rückgabetyp zu ermitteln. Es wird immer vom tatsächlichen Argument abhängen. Zur Laufzeit.Und dies schließt nicht einmal Funktionen ein, die z. B. Datenbanksuchen durchführen. Oder machen Sie Dinge basierend auf Benutzereingaben. Oder suchen Sie nach globalen Variablen, die natürlich vom dynamischen Typ sind. Oder ändern Sie den Rückgabetyp in zufälligen Fällen. Ganz zu schweigen von der Notwendigkeit, jede einzelne Funktion jedes Mal manuell zu testen.
Grundsätzlich ist es mathematisch unmöglich, den Rückgabetyp der Funktion im allgemeinen Fall zu beweisen (Halting Problem). Die einzige Möglichkeit, den Rückgabetyp zu garantieren, besteht darin, die Eingabe so einzuschränken, dass die Beantwortung dieser Frage nicht in den Bereich des Halteproblems fällt, indem Programme, die nicht nachweisbar sind, nicht zugelassen werden. Dies ist die Aufgabe der statischen Typisierung.
Statisch typisierte Sprachen haben Dinge, die "Werkzeuge" genannt werden. Dies sind Programme, die Ihnen helfen, Dinge mit Ihrem Quellcode zu tun. In diesem Fall würde ich dank Resharper einfach mit der rechten Maustaste klicken und zur Definition gehen. Oder verwenden Sie die Tastenkombination. Oder fahren Sie einfach mit der Maus darüber und es wird mir sagen, um welche Typen es sich handelt. Es ist mir nicht im geringsten wichtig, Dateien zu greifen. Ein Texteditor allein ist ein erbärmliches Werkzeug zum Bearbeiten des Programmquellcodes.
Aus dem Speicher
def funcname
würde in Python nicht ausreichen, da die Funktion beliebig neu zugewiesen werden könnte. Oder könnte in mehreren Modulen wiederholt deklariert werden. Oder im Unterricht. Etc.Das Durchsuchen von Dateien nach dem Funktionsnamen ist eine schreckliche primitive Operation, die niemals erforderlich sein sollte. Dies stellt einen grundlegenden Fehler Ihrer Umgebung und Ihrer Werkzeuge dar. Die Tatsache, dass Sie sogar in Betracht ziehen würden, eine Textsuche in Python zu benötigen, ist ein massiver Punkt gegen Python.
quelle
Denken Sie an ein Projekt mit vielen Programmierern, das sich im Laufe der Jahre geändert hat. Sie müssen dies beibehalten. Es gibt eine Funktion
Was um alles in der Welt macht es? Was ist
v
? Woher kommt das Elementanswer
?Jetzt haben wir noch ein paar Infos -; es braucht eine Art von
AnswerBot
.Wenn wir zu einer klassenbasierten Sprache gehen, können wir sagen
Jetzt können wir eine Variable vom Typ haben
AnswerBot
und die Methode aufrufen,getAnswer
und jeder weiß, was sie tut. Alle Änderungen werden vom Compiler abgefangen, bevor Laufzeitprüfungen durchgeführt werden. Es gibt viele andere Beispiele, aber vielleicht kommt Ihnen das auf die Idee?quelle
Sie scheinen einige Missverständnisse über die Arbeit mit großen statischen Projekten zu haben, die Ihr Urteilsvermögen trüben könnten. Hier sind einige Hinweise:
Die meisten Leute, die mit statisch typisierten Sprachen arbeiten, verwenden entweder eine IDE für die Sprache oder einen intelligenten Editor (wie vim oder emacs), der in sprachspezifische Tools integriert ist. In diesen Tools gibt es normalerweise eine schnelle Möglichkeit, den Typ einer Funktion zu finden. Bei Eclipse in einem Java-Projekt gibt es beispielsweise zwei Möglichkeiten, den Typ einer Methode zu ermitteln:
someVariable.
) ein, und Eclipse sucht nach dem Typ vonsomeVariable
und stellt eine Dropdown-Liste aller in diesem Typ definierten Methoden bereit. Während ich in der Liste nach unten scrolle, werden Typ und Dokumentation der einzelnen Elemente angezeigt, während sie ausgewählt sind. Beachten Sie, dass dies mit einer dynamischen Sprache sehr schwierig zu erreichen ist, da es für den Editor schwierig (oder in einigen Fällen unmöglich)someVariable
ist, den Typ zu bestimmen , sodass er nicht einfach die richtige Liste erstellen kann. Wenn ich eine Methode für verwenden möchte,this
kann ich einfach Strg + Leertaste drücken, um dieselbe Liste zu erhalten (obwohl dies in diesem Fall für dynamische Sprachen nicht so schwer zu erreichen ist).Wie Sie sehen können, ist dies etwas besser als das typische Tool, das für dynamische Sprachen verfügbar ist (nicht, dass dies in dynamischen Sprachen unmöglich ist, da einige über eine ziemlich gute IDE-Funktionalität verfügen - Smalltalk fällt mir ein -, aber es ist schwieriger für eine dynamische Sprache und daher weniger wahrscheinlich verfügbar).
Statische Sprachwerkzeuge bieten normalerweise semantische Suchfunktionen, dh sie können die Definition und Verweise auf bestimmte Symbole genau finden, ohne dass eine Textsuche durchgeführt werden muss. Wenn ich beispielsweise Eclipse für ein Java-Projekt verwende, kann ich ein Symbol im Texteditor markieren und mit der rechten Maustaste darauf klicken und entweder "Zur Definition gehen" oder "Referenzen suchen" auswählen, um eine dieser Operationen auszuführen. Sie müssen nicht nach dem Text einer Funktionsdefinition suchen, da Ihr Editor bereits genau weiß, wo er sich befindet.
Das Umgekehrte ist jedoch, dass die Suche nach einer Methodendefinition per Text in einem großen dynamischen Projekt nicht so gut funktioniert, wie Sie vorschlagen, da es in einem solchen Projekt leicht mehrere Methoden mit demselben Namen geben kann und Sie wahrscheinlich keine haben leicht verfügbare Tools, um zu unterscheiden, welches von ihnen Sie aufrufen (da solche Tools bestenfalls schwer zu schreiben oder im allgemeinen Fall unmöglich sind), müssen Sie dies also von Hand tun.
Es ist nicht unmöglich, eine REPL für eine statisch typisierte Sprache zu haben. Haskell ist das Beispiel, das mir in den Sinn kommt, aber es gibt auch REPLs für andere statisch typisierte Sprachen. Der Punkt ist jedoch, dass Sie keinen Code ausführen müssen, um den Rückgabetyp einer Funktion in einer statischen Sprache zu finden - er kann durch Prüfung ermittelt werden, ohne dass etwas ausgeführt werden muss.
Selbst wenn Sie dies tun müssten, müssten Sie wahrscheinlich nicht alles neu kompilieren . Die meisten modernen statischen Sprachen verfügen über inkrementelle Compiler, die nur den kleinen Teil Ihres Codes kompilieren, der sich geändert hat, sodass Sie bei Tippfehlern fast sofort eine Rückmeldung für Typfehler erhalten. Eclipse / Java beispielsweise hebt Tippfehler hervor, während Sie sie noch eingeben .
quelle
You seem to have a few misconceptions about working with large static projects that may be clouding your judgement.
Nun, ich bin erst 14 Jahre alt und programmiere nur ab weniger als einem Jahr auf Android, also ist es möglich, denke ich.Vergleichen Sie beispielsweise mit Javascript, Ruby oder Smalltalk, wo Entwickler zur Laufzeit die Kernsprachenfunktionen neu definieren. Dies erschwert das Verständnis des großen Projekts.
Größere Projekte haben nicht nur mehr Leute, sie haben auch mehr Zeit. Genug Zeit für alle, um zu vergessen oder weiterzumachen.
Anekdotischerweise hat ein Bekannter von mir eine sichere "Job For Life" -Programmierung in Lisp. Niemand außer dem Team kann die Codebasis verstehen.
quelle
Anecdotally, an acquaintance of mine has a secure "Job For Life" programming in Lisp. Nobody except the team can understand the code-base.
ist es wirklich so schlimm Hilft ihnen die hinzugefügte Personalisierung nicht, produktiver zu sein?Es geht nicht darum, dass Sie den Rückgabetyp vergessen - das wird immer passieren. Es geht darum, dass das Tool Sie darüber informieren kann, dass Sie den Rückgabetyp vergessen haben.
Dies ist eine Frage der Syntax, die völlig unabhängig von der statischen Typisierung ist.
Die Syntax der C-Familie ist in der Tat unfreundlich, wenn Sie eine Deklaration nachschlagen möchten, ohne über spezielle Tools zu verfügen. Andere Sprachen haben dieses Problem nicht. Siehe Rusts Deklarationssyntax:
Jede Sprache kann interpretiert werden und jede Sprache kann eine REPL haben.
Ich werde abstrakt antworten.
Ein Programm besteht aus verschiedenen Operationen, und diese Operationen sind aufgrund einiger Annahmen, die der Entwickler trifft, so angeordnet, wie sie sind.
Einige Annahmen sind implizit und andere explizit. Einige Annahmen betreffen eine Operation in ihrer Nähe, andere eine Operation außerhalb von ihnen. Eine Annahme ist leichter zu identifizieren, wenn sie explizit und so nah wie möglich an den Stellen ausgedrückt wird, an denen ihr Wahrheitswert wichtig ist.
Ein Fehler ist die Manifestation einer Annahme, die im Programm vorhanden ist, aber in einigen Fällen nicht gilt. Um einen Fehler aufzuspüren, müssen wir die falsche Annahme identifizieren. Um den Fehler zu beseitigen, müssen wir entweder diese Annahme aus dem Programm entfernen oder etwas ändern, damit die Annahme tatsächlich gilt.
Ich möchte Annahmen in zwei Arten einteilen.
Die erste Art sind die Annahmen, die abhängig von den Eingaben des Programms gelten können oder nicht. Um eine fehlerhafte Annahme dieser Art zu identifizieren, müssen wir im Raum aller möglichen Eingaben des Programms suchen. Mit fundierten Vermutungen und rationalem Denken können wir das Problem eingrenzen und auf viel kleinerem Raum suchen. Da ein Programm jedoch nur ein wenig wächst, wächst sein anfänglicher Eingaberaum mit einer enormen Geschwindigkeit - bis zu dem Punkt, an dem es für alle praktischen Zwecke als unendlich betrachtet werden kann.
Die zweite Art sind die Annahmen, die definitiv für alle Eingaben gelten oder definitiv für alle Eingaben falsch sind. Wenn wir eine solche Annahme als falsch identifizieren, müssen wir nicht einmal das Programm ausführen oder Eingaben testen. Wenn wir eine solche Annahme als richtig identifizieren, müssen wir uns um einen Verdächtigen weniger kümmern, wenn wir einen Fehler (einen beliebigen Fehler) aufspüren . Es ist daher sinnvoll, möglichst viele Annahmen zu dieser Art zu haben.
Um eine Annahme in die zweite Kategorie einzuteilen (immer wahr oder immer falsch, unabhängig von Eingaben), benötigen wir eine Mindestmenge an Informationen, um an dem Ort verfügbar zu sein, an dem die Annahme getroffen wird. Im gesamten Quellcode eines Programms werden Informationen ziemlich schnell veraltet (zum Beispiel führen viele Compiler keine Interprocedural-Analyse durch, was jeden Aufruf zu einer harten Grenze für die meisten Informationen macht). Wir brauchen eine Möglichkeit, die erforderlichen Informationen aktuell zu halten (gültig und in der Nähe).
Eine Möglichkeit besteht darin, die Quelle dieser Informationen so nah wie möglich an dem Ort zu haben, an dem sie konsumiert werden sollen. Dies kann jedoch für die meisten Anwendungsfälle unpraktisch sein. Eine andere Möglichkeit besteht darin, die Informationen häufig zu wiederholen und ihre Relevanz im gesamten Quellcode zu erneuern.
Wie Sie bereits erraten können, sind statische Typen genau das - Beacons mit Typinformationen, die über den Quellcode verteilt sind. Diese Informationen können verwendet werden, um die meisten Annahmen über die Typkorrektheit in die zweite Kategorie aufzunehmen, was bedeutet, dass fast jede Operation in Bezug auf die Typkompatibilität als immer korrekt oder immer falsch klassifiziert werden kann.
Wenn unsere Typen falsch sind, spart uns die Analyse Zeit, indem wir den Fehler eher früh als spät auf uns aufmerksam machen. Wenn unsere Typen korrekt sind, spart uns die Analyse Zeit, indem sichergestellt wird, dass wir bei Auftreten eines Fehlers Tippfehler sofort ausschließen können.
quelle
Sie erinnern sich an das alte Sprichwort "Müll rein, Müll raus". Nun, das ist es, was statische Eingabe verhindert. Es ist kein universelles Allheilmittel, aber die Strenge darüber, welche Art von Daten eine Routine akzeptiert und zurückgibt, bedeutet, dass Sie die Gewissheit haben, dass Sie richtig damit arbeiten.
Eine getAnswer-Routine, die eine Ganzzahl zurückgibt, ist daher nicht hilfreich, wenn Sie versuchen, sie in einem stringbasierten Aufruf zu verwenden. Die statische Eingabe sagt Ihnen bereits, dass Sie aufpassen müssen, dass Sie wahrscheinlich einen Fehler machen. (und sicher, Sie können es dann überschreiben, aber Sie müssen genau wissen, was Sie tun, und es im Code mithilfe einer Besetzung angeben. Im Allgemeinen möchten Sie dies jedoch nicht tun - Hacking in a runder Stift in ein quadratisches Loch funktioniert am Ende nie gut)
Jetzt können Sie mit komplexen Typen noch weiter gehen, indem Sie eine Klasse mit Erzfunktionalität erstellen, diese weitergeben und plötzlich viel mehr Struktur in Ihrem Programm erhalten. Strukturierte Programme sind viel einfacher zu verwalten und zu warten.
quelle