3 Punkte von GN⁺ 2025-11-04 | 1 Kommentare | Auf WhatsApp teilen
  • Eine Analyse des 50-zeiligen C-Codes eines K-Sprachinterpreters, den Arthur Whitney geschrieben hat, als Versuch, seinen einzigartigen Codierstil zu entschlüsseln
  • Der Code enthält zahlreiche experimentelle Strukturen, die sich von gewöhnlichem C unterscheiden, darunter makrobasierte komprimierte Syntax, nicht standardisierte C-Erweiterungen und implizite Argumentverwendung
  • Der Autor interpretiert die Bedeutung der einzelnen Makros und Funktionen selbst und untersucht dabei die Philosophie der APL-Sprachfamilie sowie Vor- und Nachteile hoher Codedichte
  • Als Stärken des Codes werden kurze Länge und hohe Ausdruckskraft genannt, als Schwächen nicht standardisierte Syntax und geringere Lesbarkeit
  • Insgesamt wird der Code als Beispiel dafür bewertet, wie wichtig nicht das „kurze Schreiben“, sondern eine Denkweise ist, bei der man das Problem vollständig versteht, bevor man Code schreibt

Arthur Whitney und sein Code

  • Arthur Whitney ist ein Informatiker, der die Sprachen A, K und Q sowie die Datenbanken kdb und Shakti entworfen hat
    • kdb ist eine in der Finanzbranche verwendete extrem schnelle Zeitreihendatenbank, Shakti ist eine noch schnellere Version, die für die Verarbeitung von Datensätzen mit einer Billion Zeilen ausgelegt ist
  • Seine Sprachen sind stark von APL beeinflusste arraybasierte Sprachen, die Kürze und mathematische Ausdruckskraft betonen
  • Im Mittelpunkt des Artikels stehen nicht Finanzanwendungen, sondern die Analyse des eigenwilligen Stils von Whitneys C-Code

Struktur des 50-zeiligen K-Interpreters

  • Das veröffentlichte Repository ksimple enthält einen etwa 50 Zeilen langen C-Interpreter, den Whitney in nur wenigen Tagen geschrieben hat
  • Der Kern des Codes besteht aus den beiden Dateien a.h und a.c; kennzeichnend sind verkürzte Funktionsdefinitionen über Makros und eine Struktur, in der Pointer wie Integer verwendet werden
  • Mit der Anweisung typedef char*s,c; werden s als String-Pointer und c als Zeichentyp definiert
  • s Q=(s)128; ist ein Beispiel dafür, dass Pointer wie Integer verwendet werden; im gesamten Code dient Q als Spezialwert für einen Fehlerzustand
  • Zahlreiche GCC-Erweiterungen wie statement expressions in der Form ({e;}) und der Operator ?: werden verwendet

Bedeutung zentraler Makros und Funktionen

  • #define _(e...) ({e;}) : ein Makro, das mehrere Anweisungen zu einem einzigen Ausdruck zusammenfasst
  • #define i(n,e) : verkürzte Schreibweise für Schleifen, um einen for-Loop in einer Zeile auszudrücken
  • #define Q(e) und ähnliche sind Makros zur Fehlerbehandlung; Qr, Qd und Qz geben jeweils Fehler für rank, domain und not-yet-implemented zurück
  • Die Makros _s, _i, f, F vereinfachen Funktionsdeklarationen und verwenden implizit die Argumente x und a
  • ax, ix, nx usw. sind Makros zur Typprüfung und Indizierung von Daten; ax prüft, ob „x ein Atom ist“
  • f(w,write(1,ax?&x:x,ax?1:strlen(x));x) ist eine Ausgabefunktion; bei einem Atom wird ein Zeichen, bei einem Vektor ein String ausgegeben

Funktionsweise des Interpreters

  • Die Funktion m(x) übernimmt Speicherallokation und die Erzeugung eines Pointers mit Längeninformation; die maximale Länge eines Vektors beträgt 255 Byte
  • Das Makro g(a,v) vereinheitlicht Atom-/Vektor-Operationen; Funktionen wie not, sub, At und _A basieren darauf
  • Das Makro G(f,o) erzeugt automatisch Funktionen für binäre Operatoren und unterstützt Operationen wie <, ==, +, *, & und |
  • cat, rev, cnt, Tak usw. sind Funktionen zur Vektormanipulation; rev erzeugt mit der Funktion ind Indizes in umgekehrter Reihenfolge
  • Die Funktion e() ist ein rekursiver Evaluator, der Strings von rechts nach links liest und einzelne Zeichen als Variablen, Zahlen und Operatoren verarbeitet
  • main() nimmt Eingaben entgegen, wertet sie mit e() aus und gibt das Ergebnis in Form einer REPL-Schleife aus

Bewertung des Codierstils

  • Vorteile
    • Ein knapper Satz primitiver Operationen, aufgebaut aus kombinierbaren Makros
    • Durch die kurze Codelänge lässt sich die gesamte Logik ohne Scrollen auf einen Blick erfassen
    • Die hochdichte Ausdrucksweise komprimiert die logische Struktur des Codes
  • Nachteile
    • Sinnentleerte Typbehandlung, bei der char* wie ein Integer verwendet wird
    • Schlechtere Lesbarkeit durch direkte Verwendung von ASCII-Codes, komplexe ternäre Operatoren und nicht standardisierte Syntax
    • Implizite Argumente und kurze Variablennamen erschweren das Erkennen der Absicht
  • Neutrale Aspekte
    • GCC-spezifische Syntax (?:, statement expression) ist interessant, verschlechtert aber die Portabilität
    • Implizite Argumentverwendung kann in kleinem Code nützlich sein, in größerem Code jedoch Verwirrung stiften
    • Kurze Namen sind effizient, wenn man sich daran gewöhnt hat, transportieren aber wenig Bedeutung

Fazit und Lehren

  • Dieser Code zeigt nicht einfach nur, „wie man kurz schreibt“, sondern eine Denkweise, bei der Code erst nach vollständigem Verständnis des Problems entsteht
  • Whitneys Code ist eine Übertragung eines bereits fertigen mathematischen Modells in Code, also das „Ergebnis, Gedanken in Code auszudrücken“
  • Der Autor reflektiert seine eigene Gewohnheit, Probleme üblicherweise erst im Code zu lösen, und
    betont künftig die Wichtigkeit konzeptioneller Modellierung und gedanklicher Strukturierung vor dem Schreiben von Code
  • Letztlich wird dieses Experiment als Erfahrung zusammengefasst, die die Fähigkeit zum Lesen von Code schult und das Gleichgewicht zwischen Codedichte und gedanklicher Klarheit untersucht

Ideen für künftige Experimente

  • Vorschläge für Übungen zur Erweiterung des Interpreters:
    • Unterstützung für Gleitkomma-Vektoren
    • Verarbeitung von mehr als 255 Elementen
    • Mehrstellige Zahlen und Variablennamen
    • Array-Literale und Ignorieren von Leerzeichen
    • Ergänzung von Speicherverwaltung und Fehleranzeigefunktionen
    • Vervollständigung nicht implementierter Funktionen
  • Solche Erweiterungen könnten ein Experiment sein, um Whitneys Codierstil beizubehalten und ihn zugleich zu einer tatsächlich nutzbaren Sprache weiterzuentwickeln

1 Kommentare

 
GN⁺ 2025-11-04
Hacker-News-Kommentare
  • Diese Makros dienen dazu, gemeinsame Operationen zu verdichten Ich habe zuerst J Incunabulum gelesen und mir danach diesen Code angesehen; für C-erfahrene Programmierer, die mitten im Code einsteigen, können die Makrodefinitionen am Anfang verwirrend sein Weil die Makros aufeinander aufbauen, erklimmt der Code schnell eine Leiter der Abstraktion Besonders gefällt mir das Iterate-Makro (i), das einen ausschweifenden Loop auf einen Buchstaben verkürzt Solch dichter Code ist schwer zu lesen, weil er innerhalb weniger Dutzend Zeilen zahllose Abstraktionen schafft und sofort verwendet Deshalb muss man ihn langsam, Zeichen für Zeichen lesen Aus der Perspektive von jemandem, der in großen Codebasen mit Hunderten dünnen Dateien gearbeitet hat, wirkt diese extreme Verdichtung sogar erfrischend

    • Ich hatte nach der Lektüre von Incunabulum einen ähnlichen Eindruck, aber als ich die Variablennamen durch Emojis ersetzt habe, wurde alles viel leichter verständlich Wie man im Codebild der Emoji-Version sieht, liegt ein Teil des Problems nicht nur in der Informationsdichte, sondern auch in der Schriftgestalt (Orthographie) Moderne Sprachen erlauben in Identifikatoren keine Symbole oder Emojis; wenn diese visuelle Unterscheidung möglich wäre, wäre der Code viel leichter lesbar Außerdem nutzen die meisten Editoren Syntax Highlighting nach Grammatik, aber tokenbasiertes Coloring (eine eigene Farbe pro Identifikator) ist oft nützlicher „Hashed syntax highlighting“ in Sublime Text ist ein Beispiel dafür Mit dieser Änderung wurde die Struktur des Codes auf einen Blick sichtbar
    • Entwickler scheinen sogar gewaltige Codebasen zu bevorzugen Ich mag Strukturen, in denen ich ohne Unterverzeichnisse direkt mit grep *.[ch] suchen kann Vor allem Java-Projekte haben zu viele kleine Dateien mit zu wenig Inhalt, was das Suchen erschwert Mit einer IDE ist es vielleicht besser, aber ich benutze keine Whitney sagte in einem Interview, dass er den ganzen Code auf einer Seite haben wolle, und seine „IDE“ seien die Windows-Konsole und Notepad
  • Um Arthur Whitneys C-Code zu verstehen, muss man zuerst eine APL-artige Sprache lernen Sonst wirkt es einfach nur wie ein seltsamer C-Stil Whitney versucht, C wie APL zu verwenden Keine Leerzeichen, einbuchstabige Namen und aus Einzeilern bestehende Funktionen – all das ist auch in APL ähnlich Es ist ein bisschen so, als würde ein Pascal-Programmierer Dinge wie #define begin { schreiben, nur dass Whitney noch viel origineller ist

    • Selbst aus Sicht eines APL-Nutzers wirkt das merkwürdig Die von Whitney entwickelte Sprache K verwendet einen solchen Stil, aber Einzeilenfunktionen waren im traditionellen APL nicht möglich Makros, ternäre Operatoren oder implizite Variablennamen gibt es in APL nicht Das Wesen von APL sind unveränderliche Array-Operationen, und Whitneys C-Stil folgt einer anderen Philosophie
    • Auf die Bemerkung „Das ist so, als ob ein Pascal-Programmierer zu C wechselt und #define begin { macht“ witzelte jemand: „Ah, wie Stephen Bourne also.“
    • Zuerst wirkte es wie eine funktionale Sprache, aber bald erinnerte es eher an den Horror des C-Präprozessors
    • Schon in der Einleitung des Artikels steht, dass „Whitneys C von APL inspiriert ist“ Der Kommentar bemängelt, dass viele Antworten bloß Zusammenfassungen seien
    • J zu lernen wäre vermutlich auch sinnvoll Es ist zugänglicher als APL und verwendet Symbole, die man auf einer normalen Tastatur eingeben kann
  • Als ich nach Shakti gesucht habe, sah ich, dass der Wikipedia-Link auf k.nyc weiterleitet, und auf der Seite stand einfach nur ein einzelnes „k“ Im Quelltext stand tatsächlich nur `k

` Das wirkt wie eine HTML-Version von Whitneys Minimalismus — alles Überflüssige wurde entfernt, und der Rest wird implizit vom Compiler erledigt

  • k

  • Unabhängig davon, wie man Whitneys Coding-Stil bewertet, ist dieser Blogpost eine hervorragende Analyse Dafür, dass der Autor ihn in acht Stunden geschrieben hat, ist er erstaunlich tiefgehend, und besonders der Schlussteil war eindrucksvoll Link zum Original

  • Das erinnert an Stephen Bournes Versuch, C wie Algol aussehen zu lassen In diesem Beispiel aus mac.h und diesem Beispiel aus expand.c spürt man eine ähnliche Ästhetik

  • In jedem Bereich gibt es zwar „best practices“, aber sie passen nur für den Durchschnittsfall gut In bestimmten Situationen kann es sogar besser sein, bewusst in die entgegengesetzte Richtung zu gehen Letztlich taugt kollektive Weisheit als Standardannahme, aber sobald man selbst zu denken beginnt, sieht man zwangsläufig die Lücken

    • Deshalb mag ich den Ausdruck „best practice“ nicht In Wahrheit ist es nur ein durchschnittlicher Kompromiss Man opfert Effizienz und Performance, um Wartbarkeit und Konsistenz zu gewinnen
    • Ein gutes Produkt zu bauen und die Lesbarkeit oder Lernkurve einer Codebasis sind getrennte Dinge Nur weil das eine gut ist, folgt das andere nicht automatisch
  • Es hat mir gefallen, dass der Beitrag dem Code gegenüber nicht aggressiv war und eine ausgewogene Perspektive gezeigt hat Ich habe ihn mit Interesse gelesen und werde ihn später wohl noch einmal lesen

  • Ich habe mich gefragt, ob dieser Coding-Stil ein bestimmtes Paradigma ist In realen Projekten habe ich solchen Code fast nie gesehen, abgesehen von Ausnahmen wie einem „business card ray tracer“ Auch der Quellcode der von Whitney entwickelten Sprache J hat einen ähnlich extrem verdichteten Stil

    • Ja, das ist Whitneys eigener Coding-Stil Er verwendet ihn konsequent in seinen Array-Sprach-Interpretern und ist dafür bekannt, ganze Implementierungen auf wenige Seiten zu komprimieren Es gibt auch einen Meta-Kommentar mit gesammelten HN-Diskussionen
    • Auf die Aussage „So einen Code habe ich in der Praxis noch nie gesehen“ antwortete jemand: „Du hattest Glück.“ Das ist nicht mehr wirklich C, sondern eher eine interne DSL, die auf C aufsetzt C ist nur noch das erste Kompilierungsziel
    • Es ähnelt APL-artigen Sprachen wie J und K Sie verwenden Nicht-ASCII-Symbole und können bei extremer Dichte sehr viel Information auf einer Seite unterbringen Wenn man sich daran gewöhnt hat, kann das auch den Vorteil haben, die Zahl der Abstraktionsebenen zu verringern
    • Es gibt auch ein Video zu co-dfns, das einen ähnlichen Ansatz verfolgt Es ist zwar nicht C, aber ebenfalls in einem hochdichten Stil geschrieben
    • Im Scherz wurde das „OCC (Obfuscated C Code)“ genannt
  • Wenn man sich die folgenden Makrodefinitionen ansieht

    #define _(e...) ({e;})
    #define x(a,e...) _(s x=a;e)
    #define $(a,b) if(a)b;else
    #define i(n,e) {int $n=n;int i=0;for(;i