1 Punkte von GN⁺ 2025-06-08 | 1 Kommentare | Auf WhatsApp teilen
  • Low-Level-Optimierung bedeutet, dem Compiler zu helfen, Absichten und Randbedingungen des Codes besser zu verstehen, und Zig eignet sich dafür gut, weil sich Typen, Alignment, Aliasing und Compile-Time-Informationen leicht explizit angeben lassen
  • Selbst optimierende Compiler wie LLVM erzeugen nicht immer den bestmöglichen Code, daher bleiben in Engpassbereichen die Prüfung des generierten Codes und Anpassungen am Code weiterhin nötig
  • Zig kann mit noalias, align, festen Array-Größen und zur Compile-Zeit übergebenen Elementtypen vektorisierten Code erzeugen, der kompakter ist als das JavaScript-Beispiel
  • comptime führt normalen Zig-Code zur Compile-Zeit aus und ermöglicht so Metaprogrammierung wie das Erzeugen von Konstanten, generische Implementierungen, Typ-Reflexion und Optimierungen bei String-Vergleichen
  • Die Stärke von Zig liegt nicht in Makros, die direkt das AST verändern, sondern in der in die Sprache integrierten Compile-Time-Ausführung; auch einige Laufzeitwerte können über zur Compile-Zeit spezialisierte Funktionen dispatcht werden

Warum man dem Compiler vertrauen, aber trotzdem nachprüfen sollte

  • Optimierung ist mehr als nur eine Technik für schnellere Programme; sie hängt auch mit Kostensenkung, besserer Skalierbarkeit und der Wahrung der Systemeinfachheit zusammen
  • Moderne Compiler liefern mit Backends wie LLVM beeindruckende Ergebnisse, erzeugen in manchen Situationen aber weiterhin suboptimalen Code
  • Low-Level-Sprachen sind nicht nur deshalb schnell, weil sie weniger Garbage Collection oder Interpreter-Overhead haben, sondern auch weil sie mehr Intentionsinformationen ausdrücken können, die der Compiler versteht
  • Compiler können weder den Algorithmus noch das Programmierparadigma selbst verändern und optimieren meist nur in einem begrenzten Rahmen wie etwa in Schleifen

Beispiel mit Array-Maximum in JavaScript und Zig

  • Das JavaScript-Beispiel speichert in der Form x[i] = y[i] > x[i] ? y[i] : x[i] das elementweise Maximum zweier Arrays in x
  • Für Menschen ist der Code klar, aber der von V8 erzeugte Bytecode ist aufgebläht
  • Das Zig-Beispiel beschreibt in den Funktionsargumenten die für die Optimierung nötigen Informationen viel genauer
    • noalias x: x ist kein Alias zu anderen Zeigern
    • *align(64): auf 64 Byte ausgerichtet
    • [65536]f64: Array-Größe und Elementtyp
    • const: schreibgeschütztes Argument
  • Dank dieser Informationen kann der Compiler besseren Code erzeugen; im Beispiel entsteht vektorisierte Assembler-Ausgabe
  • Entsprechender Rust-Code erzeugt nahezu identischen Assembler

Wo Zig bei Optimierung im Vorteil ist – und wo die Grenzen liegen

  • Zig erlaubt ausführlichere Schreibweisen, um LLVM viele Informationen über den Code zu geben
  • Die wichtigsten für Optimierung relevanten Elemente in Zig sind:
  • Das Speichermodell von Rust erlaubt es dem Compiler, grundsätzlich anzunehmen, dass Funktionsargumente kein Aliasing erzeugen, während man das in Zig explizit angeben muss
  • Wenn der Compiler bei Zig-Funktionsargumenten nicht weiß, ob kein Aliasing vorliegt, kann eine unannotierte Zig-Funktion langsamer sein als eine Rust-Funktion
  • Schon auf Basis gut annotierten LLVM-IR liefert Zig gute Ergebnisse, seine größere Stärke liegt aber in der Compile-Time-Ausführung

Die Rolle von comptime

  • comptime ist in Zig die Funktion für Code-Erzeugung zur Compile-Zeit
  • Zur Compile-Zeit sind unter anderem folgende Dinge möglich:
    • Konstanten erzeugen und in das Binary aufnehmen
    • dieselbe Hashmap-Struktur nicht für mehrere Datentypen wiederholt schreiben müssen
    • Optimierungen anstoßen, damit auf Basis von zur Compile-Zeit bekannten Daten unnötiger Code entfernt wird
    • Typen prüfen, reflektieren und erzeugen, um Generics zu implementieren
  • comptime-Code ist normaler Zig-Code, der zur Compile-Zeit ausgeführt wird; Nebeneffekte wie Netzwerk-IO sind nicht möglich
  • Die Emulationsmaschine zur Compile-Zeit entspricht dem Kompilierungsziel
  • Fast jeder Zig-Code kann mit comptime zur Compile-Zeit ausgeführt werden, und alle Typen können zur Compile-Zeit geprüft, reflektiert und erzeugt werden

Der Unterschied zu Makros

  • Das Ziel von comptime ähnelt dem von Makros, die Arbeitsweise ist jedoch anders
  • Manche Makros verändern Rohtext, andere modifizieren direkt das AST des Programms
  • Zigs comptime verändert das AST nicht direkt und bietet auch nichts wie Token-Pasting-Makros
  • Zig zielt auf eine gut lesbare Sprache ab und passt daher nicht zu Makro-Stilen, die Variablen in nicht zusammenhängenden Scopes erzeugen oder verändern
  • Was Makros können, Zig-comptime aber nicht direkt leistet:
    • andere Makros definieren
    • das AST verändern
    • Mini-Sprachen oder DSLs direkt implementieren
  • Dennoch lassen sich auch in Zig DSLs bauen; Zigs print-Funktion parst Format-Strings mit comptime, um einen Funktionsgraphen zur Serialisierung von Daten aufzubauen
  • Beispiele sind TigerBeetle account testing DSL, comath, zilliam

Optimierung von String-Vergleichen mit comptime

  • Ein gewöhnlicher String-Vergleich liefert bei unterschiedlicher Länge false zurück und vergleicht bei gleicher Länge jedes Byte der Reihe nach
  • Dazu müssen aus beiden Strings jeweils Bytes gelesen und verglichen werden
  • Häufig ist eine Seite des Vergleichs bereits zur Compile-Zeit bekannt, daher kann Zig verlangen, dass ein Argument comptime ist
    • fn staticEql(comptime a: []const u8, b: []const u8) bool
  • Beim Vergleich mit einem statischen String wie "Hello!\n" erzeugt der Compiler Code, der aus einem Längenvergleich und konstanten Vergleichen der einzelnen Bytes besteht
  • Der Zweck dieses Abschnitts ist nicht nur zu zeigen, welche Optimierung der Compiler automatisch leisten kann, sondern dass sich mit comptime eine Transformation auch erzwingen lässt, um Chancen zu eröffnen, die der Compiler sonst nicht sieht

Vergleiche in größeren Blöcken und Nutzung von SIMD

  • Ein einfacher comptime-String-Vergleich arbeitet weiterhin Byte für Byte
  • Eine verbesserte Version verwendet std.simd.suggestVectorLength(u8) oder @sizeOf(usize), um die Größe der Vergleichsblöcke festzulegen
  • Zuerst wird die String-Länge geprüft, danach werden die Anzahl der großen vergleichbaren Blöcke und die verbleibenden Bytes berechnet
  • Jeder Block wird mit einem über std.meta.Int(.unsigned, block_len * 8) erzeugten Integer-Typ per @bitCast verglichen
  • Die verbleibenden Bytes werden ebenfalls mit einem separaten Integer-Typ verglichen
  • Beim Beispiel "Hello, World!\n" nutzt der generierte Assembler größere Register und reduziert die Anzahl bedingter Verzweigungen
  • Bei längeren String-Vergleichen entsteht Assembler, der größere SIMD-Register verwendet

Laufzeitwerte zusammen mit Compile-Time-Spezialisierung nutzen

  • Zigs comptime ist nicht auf Daten beschränkt, die nur zur Compile-Zeit bekannt sind
  • In einfachen Fällen lassen sich mehrere Prozeduren zur Compile-Zeit erzeugen und zur Laufzeit abhängig vom Wert dynamisch an die passende Prozedur dispatchen
  • Im Beispielcode werden in switch (runtime_val) Werte im Bereich inline 0...100 an staticFn(comptime_val) weitergereicht, während der Rest durch runtimeFn(runtime_val) verarbeitet wird
  • Wenn keine Vergrößerung des Binarys gewünscht ist, kann auf eine vollständige Laufzeit-Implementierung zurückgefallen werden

Fazit

  • Zigs comptime ersetzt Templates, Makros, Generics und manuelle Code-Erzeugung
  • Ähnliche Dinge sind auch in anderen Sprachen möglich, in Zig ist comptime jedoch natürlicher in die Sprache integriert
  • Zig erleichtert das Schreiben performanten Codes in praktisch nützlichen Situationen und steht damit im Kontrast zu einem Turing tar-pit, in dem zwar alles möglich ist, interessante Aufgaben aber schwer sind
  • In Sprachkriegen bleibt sowohl die große Perspektive bestehen, dass Turing-Vollständigkeit allein ausreiche, als auch die Ansicht, dass Menschen trotzdem Sprachen bevorzugen dürfen
  • Aussagen wie „C ist schneller als Python“, die eine Sprache selbst zum Benchmark-Objekt machen, können irreführend sein; tatsächlich wird nicht die Sprache, sondern konkreter Code und dessen Implementierung benchmarkt

1 Kommentare

 
GN⁺ 2025-06-08
Meinungen auf Hacker News
  • Was mich an Zig am meisten reizt, ist, dass es auf einfaches Build-System, Cross-Compilation und schnelle Iterationszyklen abzielt.
    Als Spieleentwickler habe ich zwar Performance-Anforderungen, aber ich denke, dass die meisten Sprachen die nötige Performance ausreichend liefern; deshalb ist das für mich nicht das wichtigste Kriterium bei der Sprachwahl.
    In jeder Sprache kann man leistungsfähigen Code schreiben, aber entscheidend ist, ein zukunftsfähiges Framework zu wählen, mit dem sich modularer Code über Jahrzehnte warten lässt.
    C/C++ war die Standardantwort, weil es überall unterstützt wird, und Zig fühlt sich so an, als könnte es dieses Niveau erreichen.

    • Ich mag Zig, aber langfristige Wartbarkeit und Modularität sehe ich eher als eine der schwächsten Seiten.
      Zig ist Kapselung gegenüber feindlich eingestellt, und Struct-Member können nicht privat gemacht werden: https://github.com/ziglang/zig/issues/9909#issuecomment-9426...
      Das zentrale Zitat vertritt die Position: „Private Felder und Getter/Setter sind ein von Java popularisiertes Anti-Pattern; Felder sind vorhandene Daten, also sollte man sie sorgfältig benennen und als Teil der öffentlichen API dokumentieren.“
      Wenn man die interne Repräsentation nicht verbergen kann, ist es schwierig, einen API-Vertrag sauber zu schaffen, der die Grundlage von Software-Modularität bildet; man sollte die interne Repräsentation ändern können, ohne Nutzer-Code zu brechen.
      Zigs Haltung scheint zu sein, dass es keine separate interne Repräsentation geben sollte, sondern dass die Repräsentation selbst öffentlich gemacht, dokumentiert und garantiert werden muss; ich hoffe, dass diese Entscheidung irgendwann rückgängig gemacht wird und private Felder unterstützt werden.
    • Zum Spaß habe ich Zig auf einem alten Kindle-Gerät über einem abgespeckten Linux 4.1.15 ausprobiert; das war eine ziemlich interessante Erfahrung, und ich war von Zigs Reife pleasantly surprised.
      Vieles funktionierte sofort, und ich konnte mit einem alten GDB sogar seltsame Bugs debuggen.
      Ich bin ebenfalls von Zig überzeugt worden und habe hier etwas dazu geschrieben: https://news.ycombinator.com/item?id=44211041
    • Ich habe Rust ein wenig ausprobiert und mochte es, habe dann aber kurz pausiert, nachdem ich gehört hatte, dass es einen schlechten Ruf habe; als ich es wieder ausprobierte, gefiel es mir immer noch.
      Ich verstehe nicht so recht, warum es so gehasst wird.
      Unansehnliche Generics gibt es auch in C# oder TypeScript, und der Borrow Checker ist ein Konzept, das einleuchtet, wenn man schon einmal Low-Level-Arbeit gemacht hat.
    • Zig wirkt wie ein einfacheres Rust und ein besseres Go.
      Unter den auf Zig gebauten Tools beeindruckt mich bun wirklich, und seit ich bun verwende, ist mein Leben viel einfacher geworden.
      Ähnliches kann man über das in Rust gebaute uv sagen.
    • Ich bin gespannt, wie Zig auf Konsolen funktionieren wird.
      Konsolen mögen normalerweise nichts, was nicht C/C++ ist, aber da Zig nach C übersetzen kann, ist es vielleicht nicht völlig ausgeschlossen.
  • Zur Behauptung „Selbst moderne Compiler brechen die Sprachspezifikation (Clang nimmt an, dass alle Schleifen ohne Seiteneffekte terminieren)“: Ich bezweifle nicht, dass Compiler gelegentlich die Spezifikation brechen, aber in diesem Fall liegt Clang zumindest seit C11 richtig.
    In C11 steht, dass eine Schleife, deren Steuerbedingung kein konstanter Ausdruck ist und die keine Ein-/Ausgabe, keinen volatile-Zugriff, keine Synchronisation und keine atomaren Operationen ausführt, von der Implementierung als terminierend angenommen werden darf.

    • C++ sagt das bis zum Erscheinen von C++26 künftig über alle Schleifen, aber wie angemerkt gilt das für C selbst nicht; dort betrifft es nur Fälle, in denen „die Steuerbedingung kein konstanter Ausdruck ist“.
      Deshalb muss eine einfache Endlosschleife in C wie for (;;); tatsächlich als Endlosschleife kompiliert werden, und Rusts weniger opakes loop {} ebenso.
      LLVM wurde jedoch von Leuten gebaut, die manchmal vergessen, dass sie nicht immer einen C++-Compiler bauen; deshalb gab es den Punkt, an dem Rust „Gib mir eine Endlosschleife“ sagte und LLVM antwortete: „Nach C++ gibt es so etwas nicht, also optimiere ich es weg“ — und das war eine falsche Anwendung auf eine andere Sprache.
  • Um String-Vergleiche zu inlinen und zu entfalten, braucht man nicht zwingend comptime.
    Das geht auch in C: https://godbolt.org/z/6edWbqnfT
    Den Tippfehler habe ich korrigiert.

  • Ich halte den Vergleich zwischen dem JavaScript-Beispiel und den Zig/Rust-Beispielen nicht für besonders gelungen.
    Den Zig- und Rust-Compilern wird gesagt, sie sollen eine sehr moderne Ziel-CPU wählen, während V8 offenbar nicht unter denselben Bedingungen läuft.
    Ein optimierender JIT kann ebenfalls vektorisieren, wenn die Bedingungen stimmen.
    Nebenbei: Die meisten modernen Sprachen führen dieselbe Optimierung für Strings durch; ein C++-Beispiel gibt es hier: https://godbolt.org/z/TM5qdbTqh

    • Insgesamt fühlt es sich an, als würde man Äpfel mit Obstsalat vergleichen, aber um den Unterschied der Einsatzbereiche von JS und Zig zu zeigen, ist es passend.
      Das Zig-Beispiel verwendet Arrays bekannter Typen mit fester Größe, während der JS-Code zur Laufzeit „generisch“ ist, sodass x und y beliebige Objekte sein können.
      In JS muss man diesen Preis zahlen, aber ironischerweise kann man dem JIT in genau diesem Beispiel die Typinformationen besser vermitteln.
      Wenn man dafür sorgt, dass diese Funktion immer mit Float64Array derselben Größe aufgerufen wird, kann der JIT das erkennen und eine schnellere Schleife erzeugen. Es ist vielleicht keine Vektorisierung, aber deutlich besser.
      Allerdings haben Typed Arrays hohe Initialisierungskosten, weshalb man sie in der Praxis kaum nutzt, außer man allokiert einmal ein großes Typed Array und verwendet es häufig wieder.
      Außerdem hieß es im Artikel, der JS-Bytecode sei ziemlich aufgebläht; vermutlich liegt ein großer Teil daran, dass der JIT nicht garantieren kann, dass 65536 der Länge beider Arrays entspricht, und deshalb Guards einfügt.
      In der Praxis würde aber ohnehin niemand eine for-Schleife so schreiben, sondern i < x.length verwenden; in diesem Fall entfernt der JIT zumindest eine Array-Prüfung.
    • In den Godbolt-Beispielen für Rust und Zig kann man target auf eine ältere CPU umstellen.
      Sorry, dass ich die Einschränkungen des JS-Ziels nicht bedacht habe.
      Das verlinkte C++-Beispiel ist ein gutes Beispiel dafür, was Clang mit C++ leisten kann, aber selbst wenn man berücksichtigt, dass Zig für eine bestimmte CPU kompiliert, wirkt der erzeugte Assembly-Code etwas enttäuschend.
      Ein C++-Port von https://github.com/RetroDev256/comptime_suffix_automaton wäre wohl sehr interessant.
      Das ist eine Nutzung von comptime, die ein C++-Compiler nicht sauber ableiten kann.
  • Ich bin mir nicht sicher, ob der Satz „Hochsprachen fehlt die reiche Intentionalität von Low-Level-Sprachen“ wirklich stimmt.
    Intentionsausdruck scheint kein Element auf dem Hoch-/Low-Level-Spektrum zu sein; eher sollte etwas umso höherstufiger sein, je mehr Möglichkeiten es bietet, Absichten detaillierter auszudrücken.

    • Ich stimme zu und würde noch weiter gehen: Der grundlegende Unterschied zwischen Hochsprachen und Low-Level-Sprachen besteht darin, dass man in Hochsprachen Absichten ausdrückt, während man in Low-Level-Sprachen zwangsläufig die zugrunde liegenden Mechanismen ausdrückt.
    • Die hier gemeinte Absicht scheint eher „verschiebe dieses Byte um drei Stellen nach links“ zu sein, nicht „berechne den Steuersatz für diesen Kauf“.
      Es geht um die Absicht, was man die Maschine tun lassen will, weniger darum, was man erreichen möchte.
      Code wie purchase.calculate_tax().await.map_err(|e| TaxCalculationError { source: e })?; ist voller Absicht, aber man weiß nicht, welcher Maschinencode am Ende herauskommt.
  • Diese for-Schleifen-Syntax ist furchtbar.
    Zwei Listen stehen nebeneinander, und die Position eines Eintrags in der einen Liste entspricht der Position eines Eintrags in der anderen?
    Schon beim Hinsehen tun mir die Augen weh.
    Moderne Sprachen scheinen auf den falschen Weg geraten zu sein, seit sie Parsern allerlei „Magie“ hinzufügen und überall im Code kleine Symbole verstreuen.
    Das ist nichts, worauf ich stundenlang starren möchte.

    • Solche Arrays sind unabhängig von der Sprache ein sehr häufiges Muster in Low-Level-Code, ebenso wie das parallele Iterieren darüber.
      Deshalb ist es naheliegend, dass Zig eine Syntax anbietet, die genau das bequem macht und zugleich klar sichtbar lässt, was passiert.
      Ich persönlich finde das ziemlich gelungen und frage mich, warum es dir in den Augen wehtut.
  • Das Allocator-Modell von Zig gefällt mir wirklich gut.
    Ich wünschte, man könnte auch in Go statt Garbage Collection so etwas wie Request-scoped Allocators verwenden.

    • Auch in Go sind Custom Allocators und Arenen möglich und existieren tatsächlich, aber die Benutzbarkeit ist sehr schlecht und es ist schwierig, sie korrekt einzusetzen.
      Weil die Sprache selbst keine Möglichkeit hat, Ownership-Regeln auszudrücken und durchzusetzen, läuft es am Ende darauf hinaus, C mit etwas anderer Syntax zu schreiben und zu hoffen, dass alles gutgeht.
      Selbst C++ ist unter der Bedingung, dass es keine Garbage Collection gibt, deutlich sicherer als Go.
  • Das Speichermodell von Rust erlaubt dem Compiler, immer davon auszugehen, dass Funktionsargumente niemals aliasierende Referenzen sind; in Zig muss man das hingegen manuell angeben.
    Solche Alias-Angaben vermeide ich.
    Denn nur wenige verstehen sie, und wenn man sie falsch verwendet, können schwer nachvollziehbare Bugs im Code entstehen.

  • Zur Behauptung, dass die comptime-Flexibilität von Zig gute Verbesserungen in andere Sprachen gebracht habe: Funktionsausführung zur Compile-Zeit und Funktionen mit konstanten Argumenten wurden 2007 von D eingeführt und haben mehrere Sprachen dazu gebracht, Ähnliches zu übernehmen.
    https://dlang.org/spec/function.html#interpretation

  • Ich mag Zig, aber die Aussage „Ich mag die Ausführlichkeit von Zig“ klingt seltsam.
    C ist an vielen Stellen tatsächlich zu locker, aber Zig ist nach heutigen Maßstäben ein Stück zu weit in die Gegenrichtung ausgeschlagen, sodass es manchmal zu viel Rauschen durch typannotationsartige Kommentare gibt.
    Besonders gilt das für explizite Integer-Casts in Ausdrücken; dazu habe ich hier etwas geschrieben: https://floooh.github.io/2024/08/24/zig-and-emulators.html
    Wenn Zig-Code bei der Performance schneller ist als vergleichbarer C-Code, liegt das meist an den aggressiveren LLVM-Optimierungseinstellungen von Zig.
    Zig verwendet zum Beispiel standardmäßig -march=native und Whole-Program-Optimierung und kompiliert sämtlichen Zig-Code eines Projekts als eine einzige Compilation Unit.
    Fast alle „Tricks“ wie die Verwendung von unreachable als Optimierungshinweis sind auch in C möglich, nur braucht man dafür mitunter nichtstandardisierte Spracherweiterungen.
    C-Compiler, insbesondere Clang, betreiben ebenfalls sehr aggressives Constant Folding und können auch bei tiefen Call Stacks große Codebereiche auf Konstanten reduzieren, wenn das möglich ist.
    Betrachtet man also nur die Codegenerierung, gibt es oft keinen großen Unterschied zu Zig-comptime.
    Der Vorteil von comptime ist, dass es nicht stillschweigend wieder zu Runtime-Code zurückfällt; und auch Code ohne comptime ist wie in C Ziel derselben Constant-Folding-Optimierungen.
    Wird etwa eine „reine“ Nicht-comptime-Funktion mit konstanten Argumenten aufgerufen, ersetzt der Compiler den Funktionsaufruf trotzdem durch das Ergebnis.
    Kurz gesagt: Wenn C-Code langsamer ist als Zig-Code, sollte man die C-Compiler-Einstellungen prüfen. Die schwere Arbeit bei der Optimierung passiert letztlich komplett darunter in LLVM.

    • Zum Casting-Beispiel: Man kann den Cast in eine Funktion kapseln.
      fn signExtendCast(comptime T: type, x: anytype) T { const ST = std.meta.Int(.signed, @bitSizeOf(T)); const SX = std.meta.Int(.signed, @bitSizeOf(@TypeOf(x))); return @bitCast(@as(ST, @as(SX, @bitCast(x)))); }
      export fn addi8(addr: u16, offset: u8) u16 { return addr +% signExtendCast(u16, offset); }
      Das kompiliert zu derselben Assembly, ist wiederverwendbar und macht die Absicht klarer.
    • Zig hat interessante Ideen, und ich hatte erwartet, dass sich der Artikel stärker auf Low-Level-Optimierung konzentriert, aber am Ende ging es im Wesentlichen darum, dass „comptime und Whole-Program-Compilation großartig sind“.
      Dem stimme ich zu.
      Virgil konnte schon seit 2006 die gesamte Sprache zur Compile-Zeit verwenden und unterstützte ebenfalls Whole-Program-Compilation.
      Da Virgil allerdings nicht auf LLVM abzielt, wird ein Geschwindigkeitsvergleich letztlich zu einem Vergleich der beiden Compiler-Backends.
      Virgil stützt sich stark auf Reachability-Analyse und Spezialisierungsoptimierungen, die dieses Kompilierungsmodell ermöglicht.
      Zum Beispiel werden Methodenaufrufe aggressiv devirtualisiert, unerreichbare Felder und Objekte entfernt, Konstanten durch Felder und Heap-Objekte hindurch hochgestuft und polymorpher Code vollständig monomorphisiert.
    • Wenn das neue x86-Backend erscheint, könnte man vielleicht sehen, welcher Teil des Performance-Unterschieds zwischen C und Zig wirklich dem Zig-Projekt selbst zuzuschreiben ist.
    • Bei expliziten Integer-Casts scheint bald etwas aufgeräumt zu werden: https://ziggit.dev/t/short-math-notation-casting-clarity-of-...
    • In den nächsten Jahren dürfte sich die allgemeine Tendenz bei Sprachen ziemlich deutlich hin zu ausführlicheren und expliziteren Sprachen bewegen.
      Und zwar allein deshalb, weil sie dadurch für KI leichter handhabbar werden.
      Ob es eine gute Idee ist, KI fürs Coding einzusetzen, und bis zu welchem Grad, ist eine andere Frage; aber viele Entwickler glauben daran, und Sprachen werden versuchen, ihnen entgegenzukommen.