Kleine programma's en talen

Ik weet dat ik niet de enige ben die de aantrekkingskracht ziet van minuscule Forths, kleine talen, kleine programma's en in het algemeen gewoon 'kleine dingen'. Hoe komt dat eigenlijk?

De aantrekkingskracht van kleine programma's

Kleine programma's zijn toegankelijk. Als ik zie dat iets 200 regels code is, ben ik veel eerder geneigd het te lezen dan wanneer het 2.000 of 20.000 regels zijn. Bovendien, als een programma iets interessants doet in 20 regels terwijl ik 2.000 had verwacht, dan ben ik juist zeer geïntrigeerd om die 20 regels te bestuderen!

Toen ik onderzoek deed naar DOM-diffing libraries voor het maken van RetroV, kwam ik de repo ijk van Luke Jackson tegen. Ik was op dat moment gewend geraakt aan logge JavaScript-libraries, dus toen ik het bronbestand van ijk opende, was ik echt verrast. Het bronbestand (index.js) bestaat uit slechts 25 regels! Ik was direct gefascineerd.

Onlangs was een van de reacties op mijn eerder genoemde Forth-artikel die van Philippe Brochard, met een "Forth" van slechts 46 bytes. 46 bytes! Het is zo klein dat ik de hexdump hier direct kan plaatsen:

50b8 8e00 31d8 e8ff 0017 003c 0575 00ea
5000 3c00 7401 eb02 e8ee 0005 0588 eb47
b8e6 0200 d231 14cd e480 7580 c3f4

Natuurlijk springen maar weinig mensen enthousiast op de kans om ruwe machinecode te lezen. Maar dat is precies mijn punt: iets dat zo klein is, voelt minder beangstigend. Bij 46 bytes weet je dat je, als je er genoeg moeite in steekt, uiteindelijk wel kunt uitvogelen hoe het werkt.

Een volgend voorbeeld dient bijna als tegenvoorbeeld, omdat de code zo compact en angstaanjagend is, maar ik kan er niet omheen. Een van mijn favoriete softwareverhalen is Appendix A "Incunabulum" van Roger K.W. Hui uit An Implementation of J. Hierin beschrijft hij hoe Arthur Whitney een array-gebaseerde taalinterpreter schreef:

"...op één pagina en in één middag — een interpreter-fragment op de AT&T 3B1 computer. Ik heb deze interpreter ongeveer een week bestudeerd vanwege de organisatie en programmeerstijl..."

Hui kan de volledige interpreter in de appendix vermelden omdat het simpelweg die ene pagina is: 122 regels ongelooflijk cryptische C-code. Ik heb deze puzzel zelf nog niet getackeld, maar hij staat op mijn ongeschreven lijst van compacte computerdingen die ik zou uitprinten en meenemen als ik plotseling uit de samenleving zou worden verstoten en gedwongen zou worden om alleen op een berg te gaan wonen.

(Terzijde: J behoort tot de familie van array-talen, waaronder APL. Als je houdt van beknoptheid, cryptische programmeerpuzzels en hoogwaardige numerieke berekeningen, dan zijn deze talen iets voor jou!)

Update juli 2026: Spreekmakend over Arthur Whitney: Alex (alexalejandre.com) stuurde me een link die ik hier moest toevoegen. Het is een transcriptie van een presentatie van Geo Carncross over een besturingssysteem, kOS, met Whitney's k-arraytaal, de kdb-database, en een GUI en windowmanager in een opstartbaar image van 90 Kb. De statistieken van de individuele broncode zijn minstens zo indrukwekkend (zoals 60 regels C voor de genoemde GUI en windowmanager). Blijkbaar is kdb slechts 100 regels C? Zelfs voor Whitney-C is dat indrukwekkend.

Iets anders dat absoluut in mijn collectie voor die eenzame berg zou moeten zitten, is "The Most Beautiful Program Ever Written", volgens de titel van een presentatie door William Byrd. Spoiler: het is deze Lisp-interpreter in Lisp:

(define eval-expr
  (lambda (expr env)
    (pmatch expr
      [`,x (guard (symbol? x))
       (env x)]
      [`(lambda (,x) ,body)
       (lambda (arg)
         (eval-expr body (lambda (y)
           (if (eq? x y)
               arg
               (env y)))))]
      [`(,rator ,rand)
       ((eval-expr rator env)
        (eval-expr rand env))])))

Recreatie of fundamentele waarheden?

We weten allemaal dat code golfing een recreatief spel is voor programmeurs, puur voor de lol, toch? Maar is dit eigenlijk nuttig?

Ik zeg ja. Sterker nog: het is betekenisvol. Wanneer je een raytracer ziet die op een visitekaartje past, realiseer je je dat een raytracer iets is dat op een visitekaartje kan passen. Dat is een feitelijk gegeven over de minimale complexiteit van raytracers.

Kolmogorov-complexiteit is:

"...de lengte van het kortste computerprogramma (in een vooraf bepaalde programmeertaal) dat het object als output produceert."

De betekenis die ik hieruit haal, is dat code golfers ten gunste van de hele mensheid versies van programma's produceren die de minimale complexiteit van die programma's vertegenwoordigen.

Wat weten we over de fundamentele complexiteit van de Lisp-programmeertaal? Dankzij Justine Tunney weten we dat een LISP met garbage collection zo klein kan zijn als 436 bytes. Dat is een feit over Lisps met garbage collection: als je alles overbodigs weghaalt, kun je de essentie terugbrengen tot 436 bytes. Ik put hier troost uit, want dat betekent dat het concept kan worden teruggebracht tot een hanteerbaar formaat, en dat ik het waarschijnlijk in mijn hoofd kan passen.

Dankzij Frank Force weten we dat een 3D-browsergame kan passen in 2 Kb aan (extreem gecomprimeerde) JavaScript-broncode. De hele site van Martin Kleppe (aem1k.com) staat vol met wonderen die je direct in je browser kunt draaien. En we hebben nog niet eens de oppervlakte geraakt van wat de demoscene te bieden heeft voor computers die teruggaan tot de jaren 80.

Kleine dingen zijn leuk, nuttig en betekenisvol. Mijn punt is dat je de ingewikkelde mechaniek van een geminiaturiseerd of "gegolfd" programma niet per se hoeft te bestuderen om er profijt van te hebben. De miniatuur dient simpelweg als bewijs dat het concept op een visitekaartje past en in je hoofd kan passen. Zoek daarna maar naar een leesbare versie om te bestuderen.

Kleine talen

Kleine programma's zijn interessant, maar ik ben ook geïnteresseerd in kleine programmeertalen. Over het algemeen geldt: hoe kleiner de taal, hoe minder expressief deze is.

Een van de bekendste en minst expressieve talen is assembly. Assemblytalen zijn syntactisch en conceptueel zeer eenvoudig. Er is nauwelijks sprake van een "taal": het is grotendeels een rigide reeks opcode-mnemonics. Schrijven in assembly gaat vooral over het begrijpen van de puzzel van de instructieset-architectuur (ISA) van een specifieke CPU.

Een eervolle vermelding gaat naar SNOBOL, die op dat vlak lijkt op assembly vanwege de rigide syntaxis en de uitdagingen in de control flow (het begrijpt enkel sprongen en call/return). In andere opzichten is SNOBOL echter totaal anders, aangezien het een taal is voor het matchen en vervangen van strings.

Als we kijken naar syntactisch kleine "hoger-niveau" talen, zijn deze drie briljant:

  • Forths: Deze winnen bijna elke wedstrijd in "kleinheid". De enige syntactische constructie is het spatiekarakter. Forths zijn verbazingwekkend flexibel en krachtig op een manier die bijna verontrustend is.
  • Lisps (vooral Scheme): Deze hebben kleine kern-talen met een zeer eenvoudige syntaxis, maar een enorme expressieve kracht en flexibiliteit.
  • Tcl: Tcl verdient het om in één adem met Forth en Lisp genoemd te worden. Tcl is bijzonder omdat alles een string is. Je kunt in Tcl je eigen taal construeren, net als bij Forth of Lisp.

Forth, Lisp en Tcl vereisen dat je slechts een paar kernideeën begrijpt, en geven je in ruil daarvoor onbeperkte macht. Je zou de syntactische kern van deze talen op een indexkaart kunnen passen. Toch vereist het effectief gebruiken van deze tools een enorme verschuiving in denken als je gewend bent aan "normale" Algol-achtige procedurele talen.

Hoe zit het dan met iets dat zowel klein als eenvoudig is, zonder dat daar een "bloedoffer" voor nodig is? Ik denk dat de taal Lua hier goed in past. De kern van de taal is extreem compact; in de gedrukte handleiding beslaat de beschrijving van de taal slechts 27 pagina's.

C is ook vrij klein. Hoewel het niet super eenvoudig is om effectief en veilig in C te programmeren, is het onmiskenbaar compact en expressief voor zijn omvang. Zig lijkt op C, maar ik denk dat het eigenlijk vrij groot is; je moet veel van de taal kennen om het effectief te gebruiken (al is dat met een goede reden). JavaScript heeft een kern die ook vrij klein is, waarschijnlijk vergelijkbaar met Lua. Functies, arrays en objecten (gebruikt als dictionaries) vormen het grootste deel van de JS die ik gebruik.

Klein of expressief: welke kant moet je op?

In een prachtige presentatie van David Ungar, getiteld Self and Self: Whys and Wherefores, klaagt hij over de fout van de meervoudige overerving in de taal Self:

"Het is veel beter om een systeem iets te simpel te maken, zodat een persoon extra dingen moet schrijven — zelfs als hij het twee keer moet schrijven met een commentaar dat zegt: 'Kijk hier' — dan een systeem te hebben dat vreemde dingen doet en niemand begrijpt waarom. Dus: eenvoud gaat boven expressiviteit."

Ik denk dat kleine talen ook vrij expressief kunnen zijn, maar alleen met een zeer zorgvuldige ontwerpkeuze (denk aan Forth, Lisp en Tcl).

Microworlds

Het idee van kleine, zelfvoorzienende en leerbare programmeeromgevingen (microworlds) is een ander concept dat verbonden is met David Ungar. Talen die worden geleverd met volledige interactieve IDE's zijn voor de hand liggende potentiële microworlds.

Maar ik denk dat traditioneel programmeren op basis van tekstbestanden ook die microworld-ervaring kan bieden. Hiervoor heeft een taal hoogwaardige documentatie nodig die samen met de compiler/interpreter wordt geïnstalleerd. Daarnaast moet de taal streven naar foutmeldingen die begrijpelijk zijn voor beginners, inclusief uitleg over wat er misging en tips over hoe het op te lossen.

Libraries

Een ander aspect dat vaak over het hoofd wordt gezien bij de grootte van een taal, is de "standaardbibliotheek". Ik denk dat functiebibliotheken onderhevig zijn aan dezelfde afweging tussen omvang en expressiviteit. Zo was het leren van de functionele programmeerbibliotheek Ramda voor JavaScript bijna als het leren van een nieuwe programmeertaal. De combineerbaarheid en de consistentie van de curried functies waren zeer verhelderend.

Waarom zijn kleine dingen zo verrukkelijk?

Beknoptheid in onze tools heeft een bepaalde kwaliteit die verder gaat dan louter nut. Kleine dingen brengen simpelweg vreugde.

De algemene aantrekkingskracht van miniaturen

Ik ben altijd gefascineerd geweest door miniaturen, diorama's en kleine schaalmodellen. Stephanie M. Langin-Hooper schrijft in Tiny Things and Why We Love Them:

"...de handeling om iets op ware grootte te verkleinen naar een kleinere schaal heeft een beïnvloedend effect. Het object is nu schattig, toegankelijker, delicater. Het is ook hulpelozer en dus controleerbaar — troostrijk en volledig niet-bedreigend vanwege de geringe omvang."

Dit is precies de reactie die ik heb wanneer ik een klein stukje code zie dat iets nuttigs doet. Kleine schaal helpt om "grote" dingen toegankelijk te maken: wat beangstigend kan zijn op ware grootte, is beheersbaar en voorstelbaar in het klein. Miniaturen maken "experimenten met lage inzet" mogelijk.

Simon Garfield schrijft in Small world: Why we love tiny things:

"Het antwoord ligt in ons verlangen naar meesterschap en verduidelijking. Het vermogen om een leven te verrijken door een geschaalde orde en verlichting te brengen in een anders chaotische wereld — een wereld waarover we anders het gevoel hebben dat we weinig controle hebben — kan niet worden overschat. [...] In de eenvoudigste vorm laat de miniatuur ons zien hoe we meer kunnen zien, leren en waarderen met minder."

Ik denk dat dit een groot deel is van wat ik haal uit mijn "Assembly Nights", met de kleine schaal, de ordelijke logica en de interne consistentie. In tegenstelling tot de enorme, ondoorgrondelijke echte wereld, is een offline computer een fantasieplek waar ik kan geloven dat ik alles zou kunnen begrijpen als ik maar hard genoeg mijn best zou doen.

Door computergeschiedenis te bestuderen, ontdek ik dat veel van de "grote" dingen in ons vakgebied simpelweg accumulaties zijn van veel "kleine" overwinningen, waarbij individuen kleine problemen op nieuwe en andere manieren hebben opgelost.

We creëren kleine universums waarin we ons kunnen afsluiten van al het andere. Het blokkeren van het echte leven — een voorwaarde voor elke toegewijde hobby, van poppenhuisbouwer tot puzzelaar — kan contemplatief, meditatief en essentieel zijn. Mensen die zich bukken over minuscule details alsof de wereld ervan afhangt, doen dat omdat hun wereld er inderdaad van afhangt.