1 Punkte von GN⁺ 2023-10-05 | 1 Kommentare | Auf WhatsApp teilen
  • Aus über 20 Jahren Erfahrung im Schreiben von Software ergibt sich: starke statische Typisierung ist fast immer die richtige Wahl, außer bei Ausnahmen wie einer REPL oder Einweg-Skripten
  • Typen hinterlassen einen Vertrag zwischen Aufrufer und Aufgerufenem direkt im Code und filtern falsche Parameter oder Rückgabewerte schon beim Kompilieren bzw. bei der Typprüfung heraus
  • Das Beispiel, in dem der String "20" aus einem HTML-Eingabefeld wie eine Zahl verwendet wird und zu "201" wird, zeigt den Unterschied zwischen Fehlern, die vor der Laufzeit abgefangen werden, und Fehlern, die beim Kunden sichtbar werden
  • Svix versucht, Redis-Keys, Cache-Werte, Kennungen wie PersonId und PetId sowie die Validierung von API-Eingaben in das Typsystem zu legen, um Tippfehler und das Weiterreichen falscher IDs zu reduzieren
  • Wenn man Typen weglässt, kann die erste Implementierung zwar schneller gehen, aber die Kosten für Dokumentation, Tests und Debugging steigen; mit Typinferenz und IDE-Unterstützung werden Refactoring und Onboarding einfacher

Warum ich auf statische Typen bestehe

  • Starke statische Typisierung ist mehr als nur eine gute Idee; für die meiste Software ist sie nahe am richtigen Standard
  • Es gibt durchaus sinnvolle Sprachen oder Varianten ohne Typen
    • Nutzung einer REPL
    • Einweg-Skripte in Umgebungen, die ohnehin kaum typisiert sind, etwa die Shell
  • In den meisten anderen Fällen bevorzuge ich starke Typisierung
  • Auf Typen zu verzichten kann die Entwicklung kurzfristig beschleunigen, wirkt aber eher wie „mit Vollgas auf eine Klippe zufahren“
  • Am Ende gibt es nur zwei Möglichkeiten
    • mehr Arbeit investieren, um Invarianten beim Kompilieren oder bei der Typprüfung zu verifizieren
    • weniger Arbeit investieren und erst zur Laufzeit prüfen – oder gar nicht einmal dort
  • Laufzeitfehler werden während der Entwicklung nicht immer entdeckt und können, selbst wenn sie gefunden werden, in einer für Kunden sichtbaren Form auftreten
  • Tests helfen, aber es ist schwer, alle möglichen falschen Typen von Funktionsparametern zu testen; falsche Typen per Typisierung zu verhindern, ist aus dieser Sicht einfacher

Typen sind direkt mit Code-Verträgen und weniger Bugs verbunden

  • Typen sind sowohl für Menschen als auch für Werkzeuge nützliche Code-Kommentare und machen Verträge zwischen Code-Teilen strenger
  • Selbst bei derselben Funktion zum Geburtstagsgruß kann die Klarheit des Vertrags stark variieren
    • birthdayGreeting1(...params) zeigt nicht einmal die Anzahl der Parameter; ohne Dokumentation ist das Verhalten schwer zu verstehen
    • birthdayGreeting2(name, age) gibt zumindest einen Hinweis auf Name und Alter, aber ohne Typen
    • birthdayGreeting3(name: string, age: number): string nimmt auch Eingabe- und Rückgabetypen in den Vertrag auf
  • Wenn die Funktion so geändert wird, dass sie age + 1 verwendet, entsteht in der untypisierten Variante bei String-Eingaben ein Problem
    • Werte aus HTML-Eingaben können immer Strings sein
    • birthdayGreeting2("John", "20") gibt "John will turn 201 next year!" zurück
    • In der typisierten Version muss age eine Zahl sein, daher schlägt der falsche Aufruf schon beim Kompilieren fehl
  • Je größer eine Codebasis wird, desto wichtiger wird dieser Vertrag zwischen Aufrufer und Aufgerufenem
    • Man kann nachvollziehen, welche Auswirkungen Änderungen am Aufgerufenen auf den Aufrufer haben
    • Das ist besonders wichtig, wenn verschiedene Personen Aufrufer und Aufgerufenen schreiben, etwa bei Open-Source-Bibliotheken
  • Ohne solche Verträge ist schwer zu erkennen, wie weit sich Änderungen auswirken

Vorteile bei Developer Experience, Refactoring und Onboarding

  • Typinformationen werden von IDEs und Entwicklertools genutzt und verbessern die Developer Experience deutlich
  • Wenn Erwartungen schon beim Schreiben des Codes als falsch erkannt werden, sinkt die kognitive Last
  • Entwickler müssen sich nicht alle Typen aktueller Variablen und Funktionen im Kontext merken; der Compiler weist auf Abweichungen hin
  • Auch Refactoring wird einfacher
    • Wenn sich die Implementierung einer Funktion ändert, kann der Compiler anzeigen, ob dadurch Annahmen an anderer Stelle verletzt werden
  • Neue Engineers finden sich leichter in einer Codebasis oder Bibliothek zurecht
    • Über Typdefinitionen lässt sich nachvollziehen, wo etwas verwendet wird
    • Änderungen lassen sich leicht ausprobieren, weil Kompilierungsfehler sofort sichtbar werden
  • Das zeigt sich auch am Beispiel einer Funktion, die einen Person-Typ entgegennimmt
    • Bei birthdayGreeting3(person: Person) lassen sich Verwendungsstellen von Person per IDE leicht finden
    • Dass das untypisierte birthdayGreeting2(person) tatsächlich Person erwartet, erkennt man erst nach dem Lesen der gesamten Codebasis
  • Dokumentation kann einen Teil davon auffangen, aber sie veraltet leicht; Typen sind Dokumentation, die direkt im Code lebt
  • Typen sind aus dieser Sicht eine stärkere Form nützlicher Variablennamen

Wie Svix Informationen in das Typsystem legt

  • Svix versucht, möglichst viele Informationen in das Typsystem zu legen, um Fehler zu reduzieren, die schon zur Kompilierzeit auffallen können, und zugleich die Developer Experience zu verbessern
  • Redis ist im Kern ein stringbasiertes Protokoll und hat keine eingebauten Typen, wodurch die Vorteile von Typen auf der Redis-Ebene verloren gehen können
  • Das einfache Cache-Beispiel enthält zwei Bugs
    • Es gibt einen Tippfehler im Key-Namen wie person-{id} statt preson-{id}
    • Es wird versucht, Personendaten als Typ Pet zu laden
  • Um solche Probleme zu vermeiden, setzt Svix auf zwei Maßnahmen
    • Keys werden nicht als allgemeine Strings, sondern als spezifische Typen verlangt
    • Keys und Werte werden fest miteinander gekoppelt
  • Verwendet man zum Beispiel einen mit PersonCacheKey::new(id) erzeugten Key, dann schlägt Code wie cache.get(PersonCacheKey::new(id)) fehl, wenn das Ergebnis als Pet entgegengenommen werden soll
  • Auch einfache String-IDs laden zu Fehlern ein
    • do_something(id: String) macht nicht klar, welche ID gemeint ist
    • Man kann versehentlich pet.id übergeben, obwohl eigentlich pet.owner übergeben werden müsste
  • Svix verwendet deshalb für jede ID einen eigenen Typ
    • PersonId(String)
    • PetId(String)
    • owner von Pet ist ein PersonId
  • Auch die Gültigkeit von IDs aus der API ist mit der Erzeugung des Typs verknüpft
    • Eine Pet-ID hat zum Beispiel das Format eines pet_-Präfixes gefolgt von einer Ksuid
    • PetId kann nicht ohne Validierung erzeugt werden
    • Wenn auf diese Weise bei der Suche eines Pets in der Datenbank 404 Not Found zurückgegeben wird, kann man sicher sein, dass zumindest das ID-Format selbst gültig war
    • Ungültige IDs werden bereits im API-Handler mit 422 oder 400 behandelt

Gegenargumente und die Rolle von Werkzeugen

  • Die wichtigsten Gegenargumente gegen Typen sind Entwicklungsgeschwindigkeit, Lernkurve und Typkomplexität sowie zusätzlicher Aufwand und Boilerplate
  • Prototyping ohne Typen kann tatsächlich schneller sein
    • Man kann Code auskommentieren, ohne dass der Compiler sich beschwert
    • Man kann Felder vorübergehend mit falschen Werten belegen, bis die richtigen Werte feststehen
  • Aus dieser Sicht ist das jedoch aggressive und unnötige technische Schuld, deren Preis man beim Debugging lokal, in der Test-Suite und in Produktion mehrfach bezahlt
  • Eine Lernkurve gibt es, aber die meisten Menschen müssen keine Typ-Experten werden
    • Mit einfachen Typausdrücken kann man bereits gut arbeiten
    • Wenn man festhängt, kann man sich helfen lassen
  • Entwickler müssen ohnehin schon vieles lernen, etwa Programmierung selbst sowie Frameworks wie React oder Axum; deshalb wird die Belastung durch das Lernen von Typen hier als übertrieben angesehen
  • Das Lernen von Typen ist ein einmaliger Aufwand, während der Nutzen beim Onboarding in eine bestimmte Codebasis mit Hilfe von Typen größer ist
  • Ohne Typen braucht man beträchtliche zusätzliche Dokumentation und Tests, um grundlegende Stabilität zu erreichen
    • Dokumentation und Tests können veralten
    • Die passenden Typen hinzuzufügen ist aus dieser Sicht der geringere Aufwand
  • In Sprachen ohne Typinferenz kann das Schreiben von Typen mühsam sein
    • Das Java-Beispiel erzeugt Wiederholungen wie Person person1 = newPerson();
    • Im Text wurde später eine Korrektur ergänzt, dass es inzwischen auch in Java Typinferenz gibt
  • Sprachen mit Typinferenz wie Rust sind knapper
    • let person1 = new_person();
  • Um die Vorteile von Typen auszuschöpfen, braucht man einen Code-Editor oder eine IDE mit moderner Code-Vervollständigung, die die Sprache versteht
  • Anders als Geschmacksfragen wie vim vs emacs oder Tabs vs Spaces sind Typen aus dieser Sicht so vorteilhaft im Verhältnis zu ihren Kosten, dass es schwer nachvollziehbar ist, warum man sie nicht verwenden sollte
  • Als Folgebeitrag gibt es using the type system effectively

1 Kommentare

 
GN⁺ 2023-10-05
Meinungen auf Hacker News
  • Das Frustrierendste an dieser Diskussion ist, dass es ausschließlich darum geht, wie sich Menschen dabei fühlen, und dass es an empirischen Belegen fehlt.
    Die bisherige Forschung deutet darauf hin, dass es zwischen den beiden Ansätzen keinen bedeutenden Unterschied gibt, und solange es keine neuen Studien gibt, ist es schwierig, mit Sicherheit zu behaupten, dass die jeweils bevorzugte Seite eindeutig richtig liegt.
    Persönlich mag ich typisierte Sprachen, aber Typsysteme wie das von TypeScript reichen mir nicht aus. Weil man Typen zur Laufzeit nicht tatsächlich nutzen kann, bleiben Laufzeitfehler bestehen, und weil sich viel Laufzeitlogik nicht im Typsystem kodieren lässt, muss man unmögliche Fälle weiterhin selbst prüfen.
    Wenn ein Typsystem es praktisch überflüssig machen würde, über Laufzeitfehler nachzudenken, wäre das ein überwältigender Vorteil. Die meisten Sprachen erreichen dieses Niveau aber nicht, sondern bleiben in einer unklaren Mitte zwischen Overhead und einigen Vorteilen hängen.
    Der Grund, warum es bei der Zahl der Bugs oder bei der Geschwindigkeit keinen großen Unterschied gibt, scheint letztlich zu sein, dass sich die Effekte gegenseitig aufheben. Ohne Typ-Sicherheitsnetz schreibt man mehr Tests, und wenn man dem Typsystem umgekehrt zu sehr vertraut, bleiben am Ende ähnlich viele Laufzeitfehler übrig. Ich wünschte, es gäbe solide Forschung zu diesem Thema, aber es ist ein schwieriges Problem.

    • Die meisten würden wohl zustimmen, dass Typen viele Bugs verhindern, und in diesem Thread wurden auch entsprechende Studien gepostet.
      Der Kern liegt eher darin, welche subjektiven Gründe jemanden zu dem Schluss bringen, dass sich die Investition in Typen nicht lohnt.
    • Am Ende muss man sich wohl damit zufriedengeben, dass es eine Frage des Urteilsvermögens ist.
      Vor ein paar Jahren habe ich mir Forschung zur Entwicklerproduktivität angesehen, und fast alles davon war entweder ziemlich schlecht oder ließ sich nur wirklich auf Juniors anwenden. Anfänger profitieren zum Beispiel stark von schnellem Feedback zu statischen Fehlern.
      Ein gutes Experimentdesign auf Fachleute statt auf Studierende anzuwenden, ist nahezu unmöglich, und es ist schwer, das Signal herauszufiltern, weil man unzählige Variablen wie individuelle Unterschiede, Art der Entwicklung und Managementstil isolieren müsste. Traurig, aber viele Dinge im Leben lassen sich effektiv nur schwer messen.
    • Ich sehe das fast genauso.
      Der Artikel und viele Kommentare sprechen über Komfort für Programmierer, Produktivität und „Korrektheit“, aber nach dem aktuellen Forschungsstand gibt es keine aussagekräftigen Ergebnisse, dass statische Typisierung diese Dinge verbessert oder verschlechtert. Im Grunde ist das subjektiv.
      Einen Effekt, den statische Typisierung tatsächlich hat, kann man allerdings trivial nachweisen: Sie ermöglicht es, effizienteren Code zu schreiben. Das sollte im Mittelpunkt der Diskussion über Typdisziplin stehen; der Rest ist beim derzeitigen Stand eher Luftschloss.
      Das im Artikel erwähnte TypeScript ist tatsächlich nicht stark typisiert, sondern statisch, aber schwach typisiert. Typen sind dort eher wie Annotationen, ohne Garantien für Performance oder Speicherlayout. Deshalb zahlt man, abgesehen von Dokumentation, die Kosten statischer Typisierung, bekommt aber kaum echte Vorteile.
      Es ist erstaunlich, wie die Tech-Community tatsächliche Belege ignoriert und kulturelle sowie persönliche Vorlieben als Fakten akzeptiert.
    • Statische Typisierung ist nur eine Scheibe Schweizer Käse auf dem Weg zu verlässlicherer Software.
      Wie andere Techniken hat sie Löcher, daher sollte man für maximale Zuverlässigkeit mehrere Techniken kombinieren. Statische Typisierung wegzuwerfen, weil sie nicht alles abfängt, ist ungefähr so, als würde man die Tür nicht abschließen, weil ein Dieb ein Fenster einschlagen könnte. Wenn Sicherheit wirklich wichtig ist, schließt man die Tür ab und bringt Gitter vor den Fenstern an; man entscheidet sich nicht nur für eines von beidem.
    • Es wirkt, als glaubten manche, „Laufzeitfehler“ würden magisch verschwinden, wenn man Programme in einer Sprache schreibt, die Typen ausdrückt.
      Wenn eine Sprache mächtig genug ist, um allgemeine Programme zu schreiben, ist sie auch mächtig genug, um Bugs zu erzeugen.
      Statische Typen können effektiv sein, um bestimmte Arten von Bugs zu finden, aber nicht alle. Manchmal erhöhen sie die Lesbarkeit, etwa wie statische Unit-Tests oder eine domänenspezifische Sprache für ausführbare Dokumentation.
      Im Allgemeinen sind dynamische Sprachen agiler und machen es leichter, mehr Tests zu schreiben. Es gibt zwar Tests, die man in einer statisch typisierten Sprache nicht schreiben müsste, deshalb sind Typen weiterhin nützlich; sie sind aber nicht so universell mächtig, wie oft angenommen wird.
  • Abgesehen vom gesellschaftlichen Druck, statische Typen mögen zu müssen, habe ich mich letztlich immer deshalb von ihnen entfernt, weil um sie herum ein Elfenbeinturm errichtet wurde
    Ich habe in beiden Paradigmen jeweils zehn Jahre lang Software gebaut und bevorzuge inzwischen, kein Typsystem zu verwenden
    Dynamische Typen fühlen sich für mich wie ein Zwang an, einfachen Code zu schreiben – so wie Unit-Tests komponierbaren Code erzwingen. Also Code, der leicht zu lesen und zu verstehen ist
    Auch das Argument, dass Anfänger leichter in eine Codebasis einsteigen können, überzeugt mich wenig. Es fördert leicht eine Schleife, in der man nur rote Markierungen beseitigt, ohne etwas zu verstehen. Ein Typsystem legt bei jedem Projekt noch eine weitere, sehr domänenspezifische Sprache über die eigentliche Sprache, die man lernen muss, und behindert oft das Verständnis des tatsächlichen Verhaltens
    Die im Artikel genannten Probleme lassen sich auf eine Weise lösen, die genauso robust wie Typen und zugleich leichter verständlich ist. Man kann Typen zwar einfach verwenden, aber meiner Erfahrung nach passiert das in der Praxis fast nie. Autovervollständigung mag ich auch nicht; damit muss man es wohl einfach hinnehmen
    Vielleicht bin ich nur ein alter Entwickler, der „Code ist Dokumentation“ ruft, vielleicht kommt das aber auch aus einer tiefen Unzufriedenheit mit den heute in der Branche allgegenwärtigen Entwicklern vom Typ „ChatGPT hat gesagt, es stimmt, und ich bekomme ein hohes Gehalt“

    • Wenn dynamische Typen die meisten Entwickler tatsächlich dazu bringen würden, einfachen Code zu schreiben, wäre das ein sehr starkes Argument
      Im Allgemeinen scheint die Gegenbeweislage aber stärker zu sein. Typen, die nachträglich erstellt wurden, um realen dynamischen Code zu dokumentieren, sind oft viel komplexer als dieselbe Funktionalität, wenn sie von Anfang an statisch typisiert implementiert worden wäre. Das DefinitelyTyped-Ökosystem von TypeScript liefert dafür unzählige Beispiele
      Man kann schwer behaupten, dass diese Typen „einfach verwendet“ werden, aber diese Komplexität kommt nicht aus dem Typsystem selbst oder aus der Art, wie Typdefinitionen bereitgestellt werden, sondern aus der Komplexität des dynamischen Codes, den sie beschreiben
      Ein gleichwertiges Paket, das von Anfang an statisch typisiert ist, hat meist eine einfachere Schnittstelle. Denn die Typen werden vorneweg definiert, statt nachträglich in eine bestehende API eingepasst zu werden
      Ich würde sogar sagen: Wenn man eine Schnittstelle nicht explizit macht, kann man nicht wissen, ob sie einfach oder komplex ist. Dem Ideal „Code ist Dokumentation“ stimme ich zu, aber wenn es keinen Code gibt, der die Schnittstelle explizit macht, ist diese Schnittstelle definitionsgemäß unzureichend dokumentiert
    • Die Aussage „Typsysteme hindern Anfänger am Verständnis und bringen sie nur dazu, rote Markierungen zu beseitigen“ wirkt beim ersten Hören genau gegenteilig
      Ich würde so eine Szene tatsächlich gern einmal aus der Nähe sehen. In meinem Bereich ist domänenspezifische Logik in dynamisch typisierten Codebasen nahezu unverständlich, während statisch typisierter Code Entwicklern die Business-Logik beibringt
      Auch „Code ist Dokumentation“ finde ich eher verwirrend. Meiner Erfahrung nach wird Code erst durch statische Typen zur Dokumentation. Ohne sie gibt es keine Möglichkeit zu wissen, welche Eigenschaften ein Objekt hat oder warum auf eine Eigenschaft geprüft wird, von der man dachte, dass es sie nicht gibt. Kommentare gibt es zwar, aber ich habe kaum je jemanden gesehen, der aussagekräftige Kommentare hinterlässt
    • Diese Argumentation fühlt sich fast vollständig umgekehrt an, daher fällt es mir schwer, sie nachzuvollziehen
      Meine Erfahrung ist die gegenteilige. Sehr dynamische Patterns sind schwer sauber zu typisieren, und ein gutes Typsystem fördert einfachere Patterns, wodurch auch die Typen einfacher werden
      Rote Markierungen zu beseitigen ist wichtig. Eine rote Markierung bedeutet, dass es ein Problem gibt, und das ist viel einfacher, als Fehler auf andere Weise zu finden. Ich verstehe nicht, warum man diesen Fehler erst später entdecken wollen sollte
      Dass du auch Autovervollständigung nicht magst, bringt mich auf die Seite derer, die Gegnern statischer Typen nicht trauen. Ein Programmierer, der nicht möchte, dass der Computer beim Programmieren hilft, ist sehr verdächtig
    • Der Hinweis, dass Elfenbeintürme und gesellschaftlicher Druck die Einführung bestimmter Technologien unattraktiv machen, ist gut
      Das schmälert aber nicht die technischen Vorteile. Eine Technologie kann hervorragend sein, auch wenn die Leute um sie herum voller Angeberei sind
      Die Behauptung, dynamische Typen führten zu einfachem Code, klingt für mich wie: „Wenn man mit Scheuklappen fährt, fährt man langsamer, also ist das gut.“ Wenn das das Ziel ist, kann man einen Linter verwenden, der Zeilenlängen oder die Anzahl von Parametern begrenzt; man muss die Einschränkung nicht indirekt erzeugen
      Typen sind nicht die einzige Lösung, aber ihr Return on Investment ist sehr hoch, daher sind sie für mich das erste Werkzeug, das man herausholt. Die Investition ist minimal, der Nutzen groß
      „Code ist Dokumentation“ stimme ich zu, aber Typen sind ebenfalls Teil des Codes. Deshalb würde ich es so formulieren: „Code ist Dokumentation, und Typen sind Teil des Codes“
    • Ich war gerade dabei, fast dieselbe Antwort zu schreiben
      Unser Bereich ist Engineering, es gibt nicht die eine richtige Antwort, und alles ist ein Trade-off. Genau deshalb wird unsere Arbeit auch nicht sofort automatisiert und verschwinden
      Die Stimmung in diesem Thread, andere Engineers wegen ihrer Meinungen oder Erfahrungen „geringzuschätzen“, finde ich wirklich unangenehm
  • In einer Welt, in der die meisten Daten als JSON über das Netzwerk gehen, wird der Kampf für starke statische Typisierung meist ziemlich inkonsequent geführt
    Man sollte alle verfügbaren Werkzeuge nutzen, aber die meisten „Daten“ sind viel weicher, als man denkt. Menschen speichern Telefonnummern nicht aus Faulheit als Strings, sondern weil sie irgendwann geglaubt haben, daraus einen stärkeren Typ machen zu können, und damit zu oft auf die Nase gefallen sind. Bei Namen, Adressen und Postleitzahlen ist es genauso
    Solche Werte müssen von Nutzern entgegengenommen werden, und im Grunde bleibt einem nichts anderes übrig, als Text zu parsen. Wenn man ein System so baut, dass der ursprüngliche Text vor dem Parsing nicht gespeichert wird, bereut man das irgendwann fast sicher
    Am besten ist meiner Ansicht nach eine Schicht, die den ursprünglichen Eingabetext aufbewahrt und Backend-Nutzern zugleich einen typisierten Datensatz bereitstellt; ob sich diese Investition in jedem kleinen Teilbereich lohnt, muss man aber abwägen
    Wenn man schwere Auswertungen macht, braucht man sehr wahrscheinlich eine Schicht, die in SAT oder ein anderes numerisches Modell übersetzt. In dieser Welt sind Zahlen die Abstraktion. Versucht man es anders, leidet man fast zwangsläufig. Es ist besser, eine Schicht zu haben, die das Problem in eine formale Darstellung und den Lösungsraum in eine Domäne übersetzt; Typen können dabei helfen, aber allzu oft sind die „Typen“, die tatsächlich Aufmerksamkeit bekommen, nicht diese Art von Typen

    • Ich bin der Autor. Es gibt etwas an der Art, wie wir es bei Svix machen, das ich nur in einem Absatz kurz erwähnt habe und eigentlich weiter hätte erklären sollen
      Dank Bibliotheken wie Serde und Pydantic folgen wir dem Ansatz, dass Deserialisierung gleich Validierung ist. Wir validieren alle JSON-Daten, bevor wir daraus Strukturen im Code machen
      Das ähnelt dem Redis-Beispiel: Selbst wenn wir JSON über das Netzwerk empfangen, können wir sicher sein, dass es sich, sobald es nach vollständiger Validierung den Code erreicht, um wohlgeformte Typen handelt. Daher kann der Code annehmen, dass ein E-Mail-Typ eine gültige E-Mail und ein ID-Typ eine gültige ID ist
    • Auch wenn Name und Adresse bei genauer Betrachtung beide Strings sind, sollten sie trotzdem nicht einfach als Strings, sondern als separate Typen verwendet werden
      Ein Namensfeld mit einem Adressfeld zu verwechseln, ist fast immer ein Fehler, und ein Typsystem kann das erzwingen
    • Nur weil die meisten Daten als JSON übertragen werden, ist es wirklich so chaotisch? In bestimmten Fällen ist auch eine Map ein völlig vernünftiger Typ
  • Wenn ein Tippfehler zu einem Runtime-Fehler wird, ist das nicht „schneller unterwegs sein“, und wenn man beim Ändern einer Funktionssignatur die Codebase greppen muss, um alle Aufrufstellen zu finden, und dann betet, dass man sie alle repariert hat, ist das auch nicht „produktiver“
    Typen sind gut, aber alles wird zum Problem, wenn man es übertreibt. Wenn man es sich zum Lebensziel macht, die gesamte Geschäftslogik im Typsystem zu kodieren, entsteht ein unverständlicheres Chaos als ganz ohne Typen. Wenn der Typname in einer Fehlermeldung nicht in eine Zeile passt, ist man zu weit gegangen.

    • Ich habe schon völlig wahnsinnige TypeScript-Typdefinitionen gesehen
      Fairerweise muss man sagen, dass sie dazu dienten, zu altem, reinem JS-Code zu passen, und diese arme Variable konnte alle möglichen Werte enthalten
      Ich bin TypeScript ewig dankbar, aber ich wäre nicht überrascht, wenn solcher Code in Zukunft in einer Arbeit über „die Ära, in der Typen zu weit gingen“ auftaucht
    • Früher dachte ich, es sei besser, mit einem laufenden Programm zu interagieren als mit einem Compiler, weil man mit from pdb import set_trace: set_trace() interaktiv editieren konnte
      Aber sobald ein Programm auch nur etwas komplexer wird, ändert sich die Lage. Wenn man Daten über Queues zwischen Systemen weiterreicht, Async, Threads und Multiprocessing nutzt und in performancekritischen Teilen kompilierte Binary-Libraries einsetzt, wünscht man sich am Ende, man hätte alles in Erlang geschrieben
    • Ich frage mich wirklich, wie Leute arbeiten, die Änderungen an Funktionssignaturen tatsächlich so angehen
      Gibt es da eine übergreifende Methodik, etwa sehr strikte Tests mit 100 % Code Coverage?
    • Jedes Mal, wenn ich solche Beschwerden über statische Typen sehe, frage ich mich, wie die Typdefinitionen und die Architektur überhaupt gestaltet wurden, dass das zum Problem wird
      Wenn Aufrufstellen nicht beim Build oder zur Compile-Zeit gefunden werden, verwendet man keine statischen Typen
    • Ich bin mir nicht sicher, wie diese beiden Dinge mit der Typendebatte zusammenhängen
      Tippfehler können selbst in der am stärksten statisch typisierten Sprache falschen Code erzeugen. Was sollte es sonst überhaupt bedeuten, Code zu schreiben? Was ist schlimmer: ein Runtime-Fehler oder ein falsches Ergebnis ohne Fehler?
      Das Ausführen eines Python-Projekts kann schneller sein als das Kompilieren von C++, und auch dynamisch typisierte Sprachen können bessere Wege als grep bieten, um Funktionsaufrufe zu finden
  • Die Behauptung, ohne Typen sei man in der Entwicklung schneller, entspricht auch nicht meiner Erfahrung. Statische Typen machen alltägliches Programmieren schneller
    Dass IDEs dank statischer Typen besser werden, wurde weiter unten behandelt, aber ich merke es auch in der REPL. Statisch erkannte Typfehler liefern sinnvollere Fehlermeldungen, die viel näher an der eigentlichen Ursache liegen, und lassen sich schneller beheben als Runtime-Fehler
    Sie nehmen einem auch die Last, über Typen übermäßig vorsichtig nachdenken zu müssen. Der Compiler sorgt für Disziplin, also muss ich mich weniger darum kümmern. Mit der Gewissheit, dass eine große Fehlerklasse sofort abgefangen wird, kann ich schneller vorankommen
    Meiner Erfahrung nach sind statische Typsysteme einfach zu verwenden, beschleunigen die Entwicklung und erhöhen die Zuverlässigkeit. Bisher gab es nur zwei Kosten: Sie können schwerer zu lernen und schwerer zu implementieren sein

    • Stimme vollkommen zu. Das erledigt auch die Wartbarkeitsdebatte fast schon
      Ein Junior-Entwickler, den man in sechs Monaten einstellt, wird sich an untypisierten Code deutlich langsamer gewöhnen
      Ich gestehe zu, dass das erste Schreiben für manche „schneller“ sein kann, aber danach wird jeder Entwickler, der diesen Code liest, langsamer
    • Man könnte auch argumentieren, dass genau dieses vorsichtige Nachdenken Software sauberer und besser macht
      Es kann gut sein, darüber nachzudenken, was genau hineingeht und herauskommt und warum, statt nur ein Durcheinander zu erzeugen, das der Compiler akzeptiert
  • Ich denke, der Autor liegt in fast jedem Punkt falsch. Ich habe jahrzehntelang auch so gedacht, aber in den letzten Jahren meine Meinung komplett geändert
    Reduzieren Typen Bugs? Nein. Vielleicht minimal, aber nicht signifikant. Schaut euch die einschlägigen Studien an
    Bieten Typen eine bessere Developer Experience? Nein. Meine REPL und IDE haben alle Definitionen und Variablen. Alle Symbole per Autocomplete, Call Trees, Nutzungssuche, verlässliches Refactoring sowie das isolierte Ausführen, Ersetzen und Wrappen von Funktionen kann ich in der REPL und innerhalb der Anwendung erledigen
    Alles ins Typsystem kodieren? Unmöglich. Man braucht Runtime-Validierung
    Viel Glück auch dabei, Typdefinitionen wieder aufzulösen, wenn sich Anforderungen ändern. Das ist der entscheidende Punkt. Statische Typen frieren das aktuell verstandene Domänendatenmodell viel zu früh ein. Dieses Modell ändert sich, und mit Pech muss man mehrere Varianten des Domänenmodells innerhalb derselben Runtime unterstützen. Besonders wenn man Vererbung verwendet hat, wird es mühsam
    Es stimmt, dass statische Typen dem Compiler große Hebel für Optimierungen geben, aber es gibt dynamisch typisierte Sprachen, die statische Typen als optionale Zusatzfunktion anbieten
    Für viele Use Cases, besonders in der Enterprise-Entwicklung, bringen dynamische Typen in einer auf Immutability ausgerichteten funktionalen Sprache langfristig große Vorteile
    Der Kategorienfehler, den starke Typbefürworter häufig machen, besteht darin anzunehmen, man würde denselben Code einfach ohne Typen schreiben. In der Praxis schreibt man ihn nicht so

    • Am Ende ist das zwangsläufig eine frustrierende Debatte, weil alle aus ihrer eigenen Erfahrung völlig unterschiedliche Schlüsse ziehen
      In fast jedem Punkt ist mein Fazit genau entgegengesetzt. Dass Runtime-Validierung nötig ist, stimmt natürlich, aber der größte Teil davon lässt sich vermeiden
      Was Anforderungsänderungen angeht, finde ich, dass statische Typen die Anpassung eher erleichtern. In den dynamischen Typsystemen, die ich erlebt habe, waren wichtige Annahmen über Datenstrukturen überall verstreut und wurden manchmal dynamisch als Vor- oder Nachbedingungen geprüft, standen nur in Tests oder wurden gar nicht geprüft
      Um Anforderungen zu ändern, musste man die Auswirkungen auf all diese impliziten Annahmen vollständig herleiten, was Änderungen beängstigend machte. Die App mit neuem Code zu starten ist leicht, aber zu wissen, ob man einen seltenen, nicht bedachten Codepfad kaputtgemacht hat, ist sehr schwierig
      Ich bevorzuge deutlich einen Schritt statischer Analyse, der mir sagt: „Du hast dieses Interface geändert; weißt du, dass dieser Codepfad davon abhing?“ Statische Typen sind nicht der einzige Weg, aber ich halte den Aufwand für deutlich geringer, als dynamische Validierung und Tests auf demselben Niveau aufzubauen
    • Definitionen und Variablen in REPL und IDE zu sehen und Autocomplete und Refactoring zu nutzen, ist gut, funktioniert aber nur, wenn man den zu prüfenden Code tatsächlich ausführt. Meiner Erfahrung nach skaliert dieser Ansatz nicht gut
      Ein Typsystem beseitigt Runtime-Validierung nicht, aber richtig eingesetzt reduziert es sie drastisch
      Wenn sich Anforderungen ändern, ist das Beste daran, dass der Compiler einem genau sagt, was repariert werden muss, damit es wieder funktioniert. Wenn man dasselbe in einer dynamischen Sprache macht, muss man es selbst verfolgen, darauf warten, dass Unit-Tests fehlschlagen, und hoffen, dass kein Pfad durchgerutscht ist
    • Für mich war genau dieser Teil mit statischen Typen viel einfacher
      Ich konnte alle Stellen, an denen ein bestimmter Typ verwendet wurde, viel verlässlicher finden und jeweils prüfen, ob eine Änderung nötig war. In dynamisch typisierten Umgebungen war diese Arbeit deutlich kleinteiliger und mühsamer
    • Das mag deine Erfahrung sein, aber es ist nicht meine. Ich glaube nicht, dass es eine Antwort gibt, die für alle passt. Man sollte wählen, was für einen selbst funktioniert, und weitermachen
  • Als Entwickler, der Hunderttausende Zeilen in C++, Python und JS geschrieben hat, bin ich mir da auch nicht sicher. So eindeutig ist es nicht
    In allen dreien bin ich produktiv, aber Python gewinnt im Allgemeinen. Allerdings würde ich keine Game Engine oder keinen Videocodec in Python schreiben
    JavaScript ist inkonsistent und seltsam, aber das Erbe von Netscape hat uns alle längst daran gebunden
    In einem sehr objektorientierten Stil mit vielen riesigen verschachtelten Klassen können statische Typen zur Compile- und Parse-Zeit viele Fehler reduzieren. Aber ich bin zu der Ansicht gelangt, dass Objektorientierung im Allgemeinen fast eine Katastrophe ist, und einfache Funktionen und strukturierte Daten gewinnen fast immer bei Einfachheit und Wartbarkeit
    Moderne Language Server und IDEs können auch bei der JS-/Python-Entwicklung viele Tippfehler abfangen. Bei vielen Teilen der Programmierung bin ich pingelig, aber bei statischen versus dynamischen Typen hatte ich keine starke Position. Beide haben Millionen erfolgreicher Projekte

    • C++ ist, unabhängig vom Typsystem, insgesamt eine ziemlich raue Sprache, wenn es um Produktivität geht
      Den größten Produktivitätsgewinn, den eine Sprache bieten kann, sehe ich in Garbage Collection. Mich würde eher interessieren, wie Go mit seiner deutlich einfacheren Syntax und seinen einfacheren Typen im Vergleich abschneidet. Java könnte ebenfalls besser sein. Auch wenn es wortreich ist, beträgt die kognitive Last bei den meisten Aufgaben nur einen Bruchteil von C++
    • In Python programmiere ich hauptsächlich in einem funktionalen Stil, aber Typen helfen trotzdem sehr
      Ich mache viele Tippfehler und vertausche häufig die Reihenfolge von Argumenten. Besonders beim Machine Learning helfen Typen enorm. Das Letzte, was man will, ist, 30 Minuten mit Datenverarbeitung zu verbringen und dann zu sehen, wie der Trainingscode abstürzt
      Pythons graduelle Typisierung halte ich für einen sehr guten Mittelweg zwischen schnellem Prototyping und dem Ansatz, Typannotationen hinzuzufügen, wenn eine Funktion ausreichend gereift ist
    • Dieses WWDC-Video von vor ein paar Jahren über protokollorientierte Programmierung (Swift) könnte dir gefallen: https://www.youtube.com/watch?v=p3zo4ptMBiQ
  • Die Debatte darüber, dass starke Typisierung besser ist als schwache, ist geklärt, aber ob statische Typisierung besser ist als dynamische, ist nicht geklärt.
    Befürworter statischer Typen glauben, der Compiler müsse Typ-Invarianten prüfen, um „Korrektheit“ festzustellen; Befürworter dynamischer Typen halten das für Zeitverschwendung.
    Ich gehöre eindeutig zu Letzteren. Denn der Compiler kann nicht die Korrektheit des Programms prüfen, sondern nur die Korrektheit der Typen. Typkorrektheit ist für Programmkorrektheit notwendig, aber nicht hinreichend. Befürworter statischer Typen können das nicht anerkennen und bilden sich ein, statische Typisierung garantiere mehr, als sie tatsächlich tut.
    Schauen wir uns das Beispiel birthdayGreeting aus dem Artikel an. Der Autor freut sich, dass statische Typisierung bei birthdayGreeting("John", "20") einen Bug findet, weil "20" keine Zahl ist. Aber birthdayGreeting(" ", 123) wird nicht gefunden. " " ist kein Name. Auch birthdayGreeting("Anna," -12335) wird nicht gefunden. Umgekehrt wird birthdayGreeting("Anna" 4.5) gefunden, obwohl 4,5 durchaus als Alter gelten kann; das könnte man also eher als falsch bezeichnen.
    Das ist wichtig. „Typ-Bugs“ sind trivial leicht zu finden, aber semantische Bugs können sich jahrelang verstecken. Etwa ein Überlauf bei einem als uint gespeicherten Kontostand, eine Zahl, die an einer bestimmten Stelle eine Primzahl sein müsste, es aber nicht ist, oder eine Liste, die nicht leer sein darf. Nicht einmal abhängige Typen können solche Invarianten garantieren.
    Wenn das schwer zu glauben ist, sucht nach großen Bugs, die Raumfahrzeuge explodieren ließen oder Autounfälle verursachten. Soweit ich weiß, war kein einziger echter Typfehler die Ursache; die überwältigende Mehrheit waren semantische Fehler.
    [1] Die meisten verstehen nicht, dass man Typisierung entlang mindestens zweier Achsen betrachten muss, stark/schwach und statisch/dynamisch, und verwechseln schwache Typisierung ständig mit dynamischer Typisierung. C ist statisch und schwach typisiert, Python ist stark und dynamisch typisiert, JavaScript ist schwach und dynamisch typisiert.

    • Viele der Beispiele lassen sich je nach Typsystem durchaus mit statischen Typen abfangen. Wichtiger ist aber die Aussage, dass „Typ-Bugs trivial leicht zu finden sind“.
      Genau deshalb bin ich auf der Seite statischer Typisierung. Weil es so trivial ist, kann man es deklarativ, direkt neben dem zu prüfenden Code, mit sofortigem Feedback, an jeder Aufrufstelle und in jedem Teilausdruck und jeder Anweisung behandeln.
      Typannotationen bedeuten nicht, dass Semantik oder Domain-Logik korrekt sind; das muss man weiterhin testen. Aber sie können Dutzende triviale Tests ersetzen, die orthogonal zur Logik sind, die einen eigentlich interessiert. Ehrlich gesagt schreibt kaum jemand solche Tests lückenlos.
    • Ich bin der Autor. Aus meiner Sicht sind diese Beispiele keine Gegenbeispiele, sondern zeigen im Gegenteil meinen Punkt.
      Weiter hinten im Artikel habe ich gesagt, dass man validiert, wenn man Typen aus Dingen wie Benutzereingaben erzeugt. Deshalb ist der Typ Name immer gültig, und " " ist kein Name. Da der Typ einen gültigen Namen garantiert, wird das in unserer Codebasis definitiv abgefangen.
      birthdayGreeting("Anna" 4.5) und birthdayGreeting("Anna," -12335) sind in JS tatsächlich gültig, weil number eine Gleitkommazahl ist. Beim Schreiben hatte ich allerdings Ganzzahlen im Kopf. Das ist ein weiteres Beispiel dafür, dass strengere Typen als in TS, etwa Rust, dabei helfen, Invarianten besser zu definieren.
      Kurz gesagt: Beim Versuch, ein einfaches Beispiel zu zeigen, habe ich wie üblich nicht alle Typen streng definiert, und dadurch sind nur noch mehr Bugs sichtbar geworden, die durch Typen gefunden worden wären.
    • Diese Beispiele sind interessant, weil man meiner Ansicht nach einen Typ Name braucht, der immer einen gültigen Namen repräsentiert, und einen Typ Age, der immer ein gültiges Alter repräsentiert.
      Die Validierung legt man an eine einzige Stelle, nämlich in die Konstruktoren dieser Typen, und mehrere Methoden wie birthdayGreeting können Werte dieses Typs dann ohne eigene Verantwortung verwenden.
      Ich weiß nicht, wie man dieses Pattern ohne Typprüfung oder wenigstens optionale Type Hints und statische Analyse gut umsetzt. Stattdessen in jeder Methode die Eingabewerte zu validieren, ist viel zu aufwendig; und einfach anzunehmen, dass der Aufrufer gültige Werte übergibt, und dann zu testen, dass nichts Schlimmes passiert, ist ebenfalls unbefriedigend.
    • Bei der Erwähnung explodierender Raumfahrzeuge musste ich sofort an [1] denken. Dieser bekannte Unfall hatte mit Typprüfung zu tun, bei der für einige Typen zulässige Wertebereiche vorab definiert waren, und berührt auch deinen Punkt. birthdayGreeting so zu machen, dass es einen Bereich von 1 bis 150 akzeptiert, ist in Ada leicht möglich.
      Unter den öffentlich bekannten Problemen aus der Raumfahrt gibt es einige, die man mit besserer Typprüfung vermutlich hätte finden können. Bei der Umrechnung zwischen metrischen und angloamerikanischen Einheiten könnte man die Einheiten ebenfalls in die Typen aufnehmen. Im Fall von [2] lag der Fehler allerdings sehr wahrscheinlich eher bei den Integrationstests.
      Natürlich kann Typprüfung nicht alle Codeprobleme finden, insbesondere keine algorithmischen Probleme, und sie ersetzt auch keine Tests. Trotzdem sind unmittelbares Feedback und Type Hints während der Entwicklung äußerst wertvoll.
      Im Beispiel könnte man den Namens-String auch durch einen Personentyp oder ein Objekt ersetzen.
      [1]: https://en.m.wikipedia.org/wiki/Ariane_flight_V88
      [2]: https://en.m.wikipedia.org/wiki/Mars_Climate_Orbiter
    • Ich verstehe nicht, warum die Tatsache, dass der Compiler nur Typkorrektheit und nicht Programmkorrektheit prüft, ein Grund dafür sein soll, dass es Zeitverschwendung ist.
      Es gibt keine perfekte Lösung, aber es gibt viele wertvolle Lösungen.
  • Bei diesem Problem hat es eine erhebliche Konvergenz gegeben. Inzwischen bieten die meisten Sprachen auf Statement-Ebene in gewissem Maß Typinferenz. Auch C++ hat auto
    Dadurch ist der Typ-Boilerplate im Code stark zurückgegangen. Die Zeiten, in denen man in C++-for-Schleifen lange Iterator-Typen vollständig ausschreiben musste, sind vorbei
    Funktionsdeklarationen und Struct-Felder sind die Stellen, an denen man Typinformationen braucht, um den Code zu lesen. Sobald ein Programm mehr als ein paar Hundert Zeilen hat oder mehr als eine Person daran arbeitet, ist ein gewisses Maß an Annotationen unverzichtbar
    Der wichtigste Widerspruch kommt natürlich von Python- und JavaScript-Nutzern. Python hat nachträglich ein sehr seltsames, empfehlungsartiges Typsystem bekommen, und JavaScript hat nachträglich TypeScript bekommen. Beides sind nachträglich angefügte Typsysteme und werden in Umgebungen genutzt, in denen typisierter und untypisierter Code gemischt ist. Das ist schmerzhaft
    Auch LISP hat vor Jahrzehnten mit „flavors“ und dem Common LISP Object System nachträglich ein Typsystem bekommen, und das sah ebenfalls nicht gut aus. Die Lehre ist: Wenn man ein Typsystem nachträglich anflanscht, wird es chaotisch

    • Ich finde, Pythons Typsystem ist angesichts der Umstände ziemlich gut
      Es hat gute Funktionen, etwa dass Optional eine Prüfung vor der Verwendung von None erzwingt, oder strukturelles Subtyping über typing.Protocol. Wäre Python von Anfang an mit Typen im Hinterkopf entworfen worden, wäre es besser gewesen; aber angesichts der Anforderung, sich in bestehenden Python-Code zu integrieren und keinen Code kaputtzumachen, ist es ziemlich gut gelungen
      Das größere Problem bei statischen Typen in Python sind das Ökosystem und die Konventionen. Das ist besonders schlimmer geworden, weil viele Entwickler Python praktisch als Data Scientists verwenden. Aus Bequemlichkeit, ordentliche Methodensignaturen zu schreiben, missbrauchen sie *args/**kwargs
      Es ist sehr üblich, dass Methoden DataFrames oder Dictionaries wie einen Krimskrams-Beutel herumreichen. Bonuspunkte gibt es, wenn Methoden Spalten oder Felder hinzufügen bzw. löschen, sodass man erst beim Ausführen des Codes oder nach dem Lesen jeder Zeile weiß, was in diesem Datenbeutel steckt
      Natürlich kann man in fast jeder Sprache Ähnliches tun. In C# kann man alle Typen als dynamic verwenden, oder Go-Methoden können alle interface{} entgegennehmen. Aber Python hat diesen Ansatz lange aktiv gefördert, und auch heute noch stellen viele Anfänger-Tutorials „Wenn man *kwargs akzeptiert, muss man die Funktionssignatur nicht ändern“ nicht als furchtbare Falle dar, sondern als fortgeschrittenes Feature für schlaue Leute
    • Die „Typsysteme“ von Python und TypeScript wurden dafür entworfen, Typen schrittweise und in Nicht-Greenfield-Umgebungen einzuführen
      Für eine schrittweise Migration ist das unverzichtbar, und es ist völlig nachvollziehbar, dass sie so funktionieren
    • Flavors und CLOS sind keine „Typsysteme“. Sie sind „Objektsysteme“, und sie sind auch nicht besonders hässlich
      Flavors wurde in ein Lisp eingeführt, das kein Typsystem hatte; CLOS wurde später zu Common Lisp hinzugefügt, einem Lisp, das bereits ein Typsystem hatte
    • Das Typsystem von TypeScript ist wirklich erstaunlich gut. Ich wünschte, mehr Typsysteme wären so ausdrucksstark
  • Menschen sind schon immer fanatisch geworden bei Dingen, an denen sie emotional statt rational hängen
    Die Behauptung „Typen reduzieren Bugs“ ist eher etwas, das plausibel klingt, als etwas, das wahr ist
    https://blog.metaobject.com/2014/06/the-safyness-of-static-t...
    Es ist nicht so, als hätte es zu wenige Versuche gegeben. Man könnte sagen, diese Behauptung sei widerlegt worden
    Persönlich mag ich statische Typen allerdings. Vor allem wegen ihres Dokumentationseffekts, und vielleicht ist es kein Zufall, dass genau dort auch der einzige positive Effekt liegt, für den es tatsächlich belastbare empirische Evidenz gibt

    • „Beim Ändern langlebiger Codebasen reduzieren Typen Bugs“ halte ich für kaum strittig
      Regressionen zu verhindern kann wichtiger sein, als von Anfang an korrekten Code zu schreiben; wenn man Code betrachtet, der sich nicht weiterentwickelt, kann man diesen Aspekt nicht bewerten
      Konkret ist das Entfernen von Objektfeldern in großen reinen JavaScript-Projekten im Kern ein Minenfeld und hat in der Vergangenheit viele Bugs verursacht. In einem vollständigen TypeScript-Projekt kann man dieselbe Änderung dagegen mit Zuversicht vornehmen
    • Wenn man sich für eine sehr starke Behauptung ein Extrembeispiel ansehen will: Es gibt die formal verifizierte Mittelstufe von CompCert: https://users.cs.utah.edu/~regehr/papers/pldi11-preprint.pdf
      Ich denke, das kann man als empirische Evidenz dafür ansehen, dass sehr starke statische Typen Bugs reduzieren
      Letztlich sind Typen, wie viele Kommentare sagen, keine binäre Ja/Nein-Frage, sondern ein großes Spektrum entlang mehrerer Achsen wie statisch/dynamisch, stark/schwach usw. Auch zwischen Typsystemen gibt es große Unterschiede, und ebenso darin, wie Menschen diese Typsysteme auf Probleme anwenden
      Auch in statischen, stark typisierten Sprachen kann man alles als Strings darstellen und ständig konvertieren; das ist im Grunde Arbeiten wie in einer dynamisch typisierten Sprache. Umgekehrt kann man Vorteile erzielen, wenn man die Werkzeuge des Typsystems nutzt, Klassen für gültige Werte erstellt und wichtige Invarianten behauptet
    • Ich bevorzuge starke statische Typen sehr viel stärker, weil sie Code selbstdokumentierend machen
      Die Produktivität steigt nicht um ein Mehrfaches, sondern um Größenordnungen. Das sage ich als jemand, der viele Sprachen intensiv genutzt hat, die einen breiten Bereich dieses Spektrums abdecken: C, C++, Java, Python, JavaScript, TCL und andere
      Code, den man in letzter Zeit nicht angefasst hat, ob im aktuellen Projekt oder in Abhängigkeiten, lässt sich viel leichter durchdenken. Man muss nicht ständig Umwege nehmen, um herauszufinden, was genau man mit einem Objekt tun kann, das eine Funktion zurückgegeben hat, und kann sich stärker auf das eigentliche Problem konzentrieren
      Es gibt auch die angenehme Erleichterung, wenn die Kompilierung durchläuft, aber das ist zweitrangig
    • Das Argument für statische Typen ist, dass sie, weil Typen statisch sind, Typfehler unmöglich machen
      Eine emotionale Fixierung gibt es höchstens in der Wut angesichts von „Aber sie beseitigen doch nicht alle Bugs!“
    • Wie würde es sich anfühlen, wenn jemand behauptet: „Emotionale Argumente lassen sich schneller formulieren als rationale Argumente“?
      Genau so fühlt sich diese Debatte an
      Ich will einfach die simple Rationalität, dass der Compiler „Nein“ sagt, wenn ich versuche, eine Hashmap wie Apple oder String zu behandeln