- 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
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 erfrischendgrep *.[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 NotepadUm 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#define begin {macht“ witzelte jemand: „Ah, wie Stephen Bourne also.“Als ich nach Shakti gesucht habe, sah ich, dass der Wikipedia-Link auf
k.nycweiterleitet, 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.hund diesem Beispiel ausexpand.cspürt man eine ähnliche ÄsthetikIn 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
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
Wenn man sich die folgenden Makrodefinitionen ansieht