Ontdek hoe John McCarthy’s symbolische aanpak en Lisp-ontwerpideeën—lijsten, recursie en garbage collection—AI en modern programmeren beïnvloedden.

Dit is geen museumtour van “oude AI.” Het is een praktische les in geschiedenis voor iedereen die software bouwt—programmeurs, tech leads en productbouwers—omdat John McCarthy’s ideeën hebben beïnvloed hoe we denken over het doel van programmeertalen.
Lisp was niet alleen een nieuwe syntaxis. Het was een gok dat software ideeën kon manipuleren (niet alleen getallen) en dat ontwerpkeuzes in talen onderzoek, productiteratie en hele tooling-ecosystemen konden versnellen.
Een nuttige manier om McCarthy’s nalatenschap te lezen is als een vraag die vandaag de dag nog telt: hoe direct kunnen we intentie omzetten in een uitvoerbaar systeem—zonder te verdrinken in boilerplate, wrijving of accidentele complexiteit? Die vraag echoot van Lisp’s REPL tot aan moderne “chat-naar-app”-workflows.
John McCarthy wordt niet alleen herinnerd voor het helpen opstarten van AI als onderzoeksveld, maar voor het aandringen op een specifiek soort AI: systemen die ideeën kunnen manipuleren, niet slechts antwoorden berekenen. Midden jaren 50 organiseerde hij het Dartmouth Summer Research Project (waar de term “artificial intelligence” werd voorgesteld) en later beïnvloedde hij AI-werk bij MIT en Stanford. Maar zijn meest blijvende bijdrage is misschien de vraag die hij bleef stellen: wat als redeneren zelf als een programma te uiten is?
De meeste vroege successen in computing waren numeriek: ballistieke tabellen, engineeringssimulaties, optimalisatie en statistiek. Die problemen passen netjes in rekenkunde.
McCarthy richtte zich op iets anders. Menselijk redeneren werkt vaak met concepten als “als”, “omdat”, “behoort tot”, “is een soort” en “alle dingen die aan deze voorwaarden voldoen.” Dat zijn niet gemakkelijk te vangen als floating-point waarden.
McCarthy’s aanpak beschouwde kennis als symbolen (namen, relaties, categorieën) en zag denken als regel-gebaseerde transformaties over die symbolen.
Een hoog-niveau manier om het te zien: numerieke benaderingen beantwoorden “hoeveel?” terwijl symbolische benaderingen proberen te beantwoorden “wat is het?” en “wat volgt uit wat we weten?”
Als je gelooft dat redeneren programmeerbaar kan zijn, heb je een taal nodig die comfortabel expressies kan representeren zoals regels, logische uitspraken en geneste relaties—en die ze vervolgens kan verwerken.
Lisp werd gebouwd om dat doel te dienen. In plaats van ideeën in starre, voorgevormde datastructuren te duwen, maakte Lisp het natuurlijk om code en kennis in vergelijkbare vormen te representeren. Die keuze was niet academische stijl—het was een praktische brug tussen het beschrijven van een gedachte en het uitvoeren van een procedure, precies de brug die McCarthy wilde dat AI zou oversteken.
Wanneer McCarthy en vroege AI-onderzoekers “symbolisch” zeiden, bedoelden ze geen mysterieuze wiskunde. Een symbool is simpelweg een betekenisvol label: een naam zoals customer, een woord zoals hungry, of een tag zoals IF en THEN. Symbolen zijn belangrijk omdat ze een programma in staat stellen te werken met ideeën (categorieën, relaties, regels) in plaats van alleen ruwe getallen.
Een eenvoudige vergelijking: spreadsheets zijn geweldig wanneer je wereld kolommen en rekenwerk is. Symbolische systemen zijn handig wanneer je wereld regels, categorieën, uitzonderingen en structuur bevat.
In veel programma’s is het verschil tussen 42 en "age" niet alleen het datatype—het is wat de waarde voorstelt. Een symbool geeft je iets dat je kunt vergelijken, opslaan en combineren zonder betekenis te verliezen.
Dat maakt het natuurlijk om zaken te representeren als “Parijs is een stad” of “als de batterij leeg is, zoek een oplader.”
Om iets nuttigs met symbolen te doen, heb je structuur nodig. Lisp populariseerde een heel eenvoudige structuur: de lijst. Een lijst is gewoon een geordende groep items, en die items kunnen zelf weer lijsten zijn. Met dat ene idee kun je zinnen, formulieren en boomvormige kennis representeren.
Hier is een klein conceptueel voorbeeld (in Lisp-achtige stijl):
(sentence (subject robot) (verb needs) (object power))
Het leest bijna als Engels: een zin bestaande uit onderwerp, werkwoord en lijdend voorwerp. Omdat het gestructureerd is, kan een programma (subject robot) ophalen of (object power) vervangen door iets anders.
Zodra informatie in symbolische structuren staat, worden klassieke AI-taken benaderbaar:
IF een patroon matcht, THEN concludeer iets nieuws.De sleutelverschuiving is dat het programma niet alleen rekent; het manipuleert betekenisvolle kennisstukken in een vorm die het kan inspecteren en transformeren.
Lisp’s ontwerpkeuzes bleven niet binnen de academie. Ze beïnvloedden hoe mensen tools bouwden en hoe snel ze ideeën konden verkennen:
Die eigenschappen leiden vaak tot ecosystemen waar experimenteren goedkoop is, prototypes sneller producten worden, en teams zich kunnen aanpassen wanneer eisen veranderen.
Lisp begon met een heel praktisch ontwerpprobleem: hoe schrijf je programma’s die net zo natuurlijk met symbolen kunnen werken als met getallen?
McCarthy probeerde geen “betere rekenmachine” te bouwen. Hij wilde een taal waarin een expressie zoals (is (parent Alice Bob)) opgeslagen, geïnspecteerd, getransformeerd en beredeneerd kon worden net zo eenvoudig als (+ 2 3).
De prioriteit was symbolische informatie makkelijk te representeren en manipuleren. Dat leidde tot focus op lijsten en boomachtige structuren, omdat die goed aansluiten bij hoe mensen betekenis uitdrukken: zinnen, logische regels, geneste categorieën en relaties.
Een ander doel was het klein en consistent houden van de kern van de taal. Wanneer een taal minder “speciale gevallen” heeft, besteed je minder tijd aan het onthouden van regels en meer tijd aan het samenstellen van ideeën. Lisp zette in op een klein aantal bouwstenen die gecombineerd konden worden tot grotere abstracties.
Een belangrijk inzicht was dat programma’s en data dezelfde soort structuur kunnen delen. Simpel gezegd: als je data een geneste lijst is, kan je programma ook een geneste lijst zijn.
Dat betekent dat je kunt:
Lisp populariseerde ook een mindset: talen hoeven geen one-size-fits-all te zijn. Ze kunnen rond een probleemdomein worden ontworpen—zoals redeneren, search en kennisrepresentatie—en toch decennialang invloed hebben op algemene programmering.
S-expressies (kort voor symbolic expressions) zijn Lisp’s kenmerkende idee: één consistente manier om code en data als geneste lijsten te representeren.
In één oogopslag is een S-expressie gewoon haakjes rond items—sommige items zijn atomen (zoals namen en getallen) en sommige items zijn zelf lijsten. Die regel van “lijsten in lijsten” is het hele punt.
Omdat de structuur uniform is, worden Lisp-programma’s opgebouwd uit dezelfde bouwstenen tot op het diepste niveau. Een functieroep, een configuratie-achtig stukje data en een stuk programmastructuur kunnen allemaal als een lijst worden uitgedrukt.
Die consistentie betaalt zich direct uit:
Ook al schrijf je nooit Lisp, dit is een belangrijk ontwerplesje: wanneer een systeem is opgebouwd uit één of twee voorspelbare vormen, besteed je minder tijd aan randgevallen en meer tijd aan bouwen.
S-expressies moedigen compositie aan omdat kleine, leesbare stukjes op natuurlijke wijze samenvoegen tot grotere. Wanneer je programma “gewoon geneste lijsten” is, betekent ideeën combineren vaak een expressie in een andere nesten of lijsten uit herbruikbare delen samenstellen.
Dit duwt je naar een modulaire stijl: schrijf kleine operaties die één ding doen en stapel ze daarna om een groter doel uit te drukken.
Het voor de hand liggende nadeel is onwennigheid. Voor veel nieuwkomers lijkt haakjes-volle syntaxis vreemd.
Maar het voordeel is voorspelbaarheid: zodra je de nestingsregels begrijpt, zie je betrouwbaar de structuur van een programma—en tools ook. Die helderheid is een grote reden dat S-expressies verder reikten dan Lisp zelf.
Recursie is het makkelijkst te begrijpen met een alledaagse metafoor: een rommelige kamer opruimen door er kleinere “kamers” van te maken. Je probeert niet alles tegelijk op te lossen. Je pakt één item, legt het op zijn plek, en herhaalt dezelfde actie op wat overblijft. De stappen zijn simpel; de kracht komt van het herhalen tot er niets meer te doen is.
Lisp leunt op dit idee omdat veel van zijn data van nature uit lijsten bestaat: een lijst heeft een “eerste ding” en “de rest.” Die vorm past perfect bij recursief denken.
Om een lijst te verwerken, handel je het eerste element af en pas je dezelfde logica toe op de rest van de lijst. Wanneer de lijst leeg is, stop je—dat is het duidelijke “niets meer te doen”-moment dat recursie begrijpelijk maakt in plaats van mysterieus.
Stel dat je de som van een lijst getallen wilt hebben.
Dat is alles. De definitie leest als gewoon Nederlands, en de programmastructuur weerspiegelt het idee.
Symbolische AI representeert vaak expressies als boomachtige structuren (een operator met subexpressies). Recursie is een natuurlijke manier om die boom te “bezoeken”: evalueer het linker-onderdeel op dezelfde manier als het rechter-onderdeel, en ga door tot je een eenvoudige waarde bereikt.
Deze patronen hielpen later het functionele programmeren vormen: kleine functies, duidelijke basisgevallen en datatransformaties die makkelijk te begrijpen zijn. Zelfs buiten Lisp leidt de gewoonte om werk op te delen in “doe één stap, herhaal op de rest” tot schonere programma’s en minder verborgen bijwerkingen.
Vroege programmeurs moesten vaak geheugen handmatig beheren: ruimte alloceren, bijhouden wie “eigenaar” is en onthouden dat je het op het juiste moment vrijmaakt. Dat werk vertraagt ontwikkeling en creëert een klasse bugs die moeilijk te reproduceren en makkelijk te publiceren zijn: leaks die langzaam prestaties aantasten en dangling pointers die een programma laten crashen lang nadat de oorspronkelijke fout is gemaakt.
John McCarthy introduceerde garbage collection voor Lisp als een manier om programmeurs te laten focussen op betekenis in plaats van administratie.
Op hoofdlijnen vindt GC automatisch stukken geheugen die niet meer bereikbaar zijn vanuit het draaiende programma—waarden die niets meer kan gebruiken—en geeft die ruimte terug.
In plaats van te vragen “hebben we elk object precies één keer vrijgegeven?”, verschuift GC de vraag naar “is dit object nog toegankelijk?”. Als het programma er niet bij kan, is het vuilnis.
Voor symbolisch AI-werk maken Lisp-programma’s vaak veel kortlevende lijsten, bomen en tussenresultaten. Handmatig geheugenbeheer zou experimentatie veranderen in een constante strijd met resource-cleanup.
GC verandert de dagelijkse ervaring:
Het kernidee is dat een taalfeature een teamvermenigvuldiger kan zijn: minder tijd kwijt aan het debuggen van mysterieuze corruptie betekent meer tijd om de echte logica te verbeteren.
McCarthy’s keuze bleef niet beperkt tot Lisp. Veel latere systemen adopteerden GC (en varianten ervan) omdat de afweging zich vaak terugbetaalt: Java, C#, Python, JavaScript-runtimes en Go vertrouwen op garbage collection om grootschalige ontwikkeling veiliger en sneller te maken—zelfs wanneer performance belangrijk is.
In Lisp is een expressie een stuk code geschreven in een consistente vorm (vaak een lijst). Evaluatie is simpelweg het proces van beslissen wat die expressie betekent en wat ze oplevert.
Bijvoorbeeld, wanneer je iets schrijft als “tel deze getallen op” of “roep deze functie aan met deze inputs”, volgt de evaluator een klein stel regels om die expressie om te zetten in een resultaat. Zie het als de scheidsrechter van de taal: die beslist wat er vervolgens gebeurt, in welke volgorde en wanneer te stoppen.
McCarthy’s slimme zet was niet alleen nieuwe syntaxis uitvinden—het was het compact en regelmatig houden van de “betekenismotor”. Wanneer de evaluator uit een paar duidelijke regels bestaat, gebeuren twee goede dingen:
Die consistentie is een reden waarom Lisp een speelplaats werd voor ideeën in symbolische AI: onderzoekers konden snel nieuwe representaties en controle-structuren proberen zonder op een compilerteam te wachten.
Macros zijn Lisp’s manier om repetitieve codestructuren te automatiseren, niet alleen repetitieve waarden. Waar een functie je helpt berekeningen niet te herhalen, helpt een macro je herhaalde structuren te vermijden—veelvoorkomende patronen zoals “doe X, maar log het ook” of “definieer een mini-taal voor regels”.
Het praktische effect is dat Lisp nieuwe gemakken van binnenuit kan laten groeien. Veel moderne tools weerspiegelen dit idee—templatesystemen, codegenerators en metaprogrammering—omdat ze hetzelfde doel ondersteunen: snellere experimentatie en duidelijkere intentie.
Als je benieuwd bent hoe deze mindset alledaagse ontwikkelworkflows beïnvloedde, zie /blog/the-repl-and-fast-feedback-loops.
Een groot deel van Lisp’s aantrekkingskracht was niet alleen de taal—maar hoe je ermee werkte. Lisp populariseerde de REPL: Read–Eval–Print Loop. In gewone termen is het alsof je een gesprek voert met de computer. Je typt een expressie, het systeem voert die meteen uit, print het resultaat en wacht op je volgende invoer.
In plaats van een heel programma te schrijven, te compileren, te draaien en vervolgens te zoeken wat stuk is, kun je ideeën stap voor stap proberen. Je definieert een functie, test die met een paar inputs, pas aan en test opnieuw—allemaal binnen enkele seconden.
Dat ritme moedigt experimenten aan, wat vooral belangrijk was voor vroeg AI-werk waar je vaak niet van tevoren wist wat de juiste aanpak was.
Snelle feedback verandert “grote weddenschappen” in “kleine checks.” Voor onderzoek maakt het gemakkelijker hypotheses te verkennen en tussentijdse resultaten te inspecteren.
Voor productprototyping verlaagt het de kosten van iteratie: je kunt gedrag met echte data valideren, randgevallen eerder opmerken en features verfijnen zonder te wachten op lange buildcycli.
Dit is ook waarom moderne vibe-coding tools aantrekkelijk zijn: ze comprimeren feedback-lussen agressief. Bijvoorbeeld, Koder.ai gebruikt een chatinterface (met een agentgebaseerde architectuur onder de motorkap) om productintentie snel om te zetten in werkende web-, backend- of mobiele code—waardoor de “probeer → pas aan → probeer opnieuw”-lus meer op een REPL gaat lijken dan op een traditioneel pipeline-proces.
Het REPL-idee duikt vandaag op in:
Verschillende tools, hetzelfde principe: verkort de afstand tussen denken en zien.
Teams profiteren het meest van REPL-achtige workflows wanneer ze onzekere eisen verkennen, data-gedreven features bouwen, API’s ontwerpen of lastige logica debuggen. Als het werk draait om snel leren—over gebruikers, data of randgevallen—is interactieve feedback geen luxe; het is een vermenigvuldiger.
Lisp “won” niet door ieders dagelijkse syntaxis te worden. Het won door ideeën te zaaien die stilletjes normaal werden in veel ecosystemen.
Concepten die Lisp als standaard behandelde—functies als waarden, veelvuldig gebruik van higher-order operaties en de voorkeur voor programma’s opgebouwd uit kleine onderdelen—komen nu veel voor. Zelfs talen die er niks uitzien als Lisp moedigen map/filter-achtige transformaties, immutabele data-gewoonten en recursie-achtig denken aan (vaak via iterators of folds).
De belangrijke mentale verschuiving is: zie datatransformaties als pijplijnen en gedrag als iets dat je kunt doorgeven.
Lisp maakte het makkelijk om programma’s als data te representeren. Die denkwijze zie je vandaag in hoe we ASTs (abstract syntax trees) bouwen en manipuleren voor compilers, formatters, linters en codegenerators. Als je met een AST werkt, doe je iets dat nauw verwant is aan “code als data”, zelfs als de structuren JSON-objects, getypeerde knopen of bytecodegrafen zijn.
Dezelfde symbolische aanpak stuurt praktische automatisering aan: configuratieformaten, templating-systemen en buildpijplijnen vertrouwen allemaal op gestructureerde representaties die tools kunnen inspecteren, transformeren en valideren.
Moderne Lisp-familietalen (in bredere zin: hedendaagse Lisps en Lisp-geïnspireerde tools) blijven invloed uitoefenen op hoe teams interne DSLs ontwerpen—kleine, gefocuste mini-talen voor tests, deployment, data-wrangling of UI.
Buiten Lisp proberen macro-systemen, metaprogrammeringsbibliotheken en codegeneratie-frameworks hetzelfde resultaat: de taal uitbreiden om het probleem beter te vangen.
Een pragmatisch advies: syntaxisvoorkeuren veranderen, maar duurzame ideeën—symbolische structuur, composeerbare functies en uitbreidbaarheid—blijven hun waarde behouden over decennia en codebases.
Lisp heeft een reputatie die oscilleert tussen “briljant” en “onleesbaar”, vaak gebaseerd op tweedehands indrukken in plaats van dagelijkse ervaring. De realiteit is gewooner: Lisp maakt keuzes die in de juiste context krachtig zijn en in andere onhandig.
Voor nieuwkomers kan Lisp’s uniforme syntaxis voelen alsof je naar de “binnenkant” van een programma kijkt in plaats van naar een gepolijnd oppervlak. Die ongemakkelijkheid is echt, vooral als je gewend bent aan talen waar syntaxis verschillende constructies visueel scheidt.
Historisch gezien is Lisp’s structuur echter juist de bedoeling: code en data delen dezelfde vorm, wat programma’s eenvoudiger maakt om te transformeren, genereren en analyseren. Met goede editor-ondersteuning (indentatie, structurele navigatie) wordt Lisp-code vaak meer op vorm gelezen dan door haakjes te tellen.
Een veelvoorkomend stereotype is dat Lisp per definitie traag is. Historisch hadden sommige implementaties inderdaad moeite vergeleken met low-level talen, en dynamische features kunnen overhead toevoegen.
Maar het is niet accuraat om “Lisp” als één performance-profiel te behandelen. Veel Lisp-systemen ondersteunen al lang compilatie, type-declaraties en serieuze optimalisatie. Een nuttiger vraag is: hoeveel controle heb je nodig over geheugenindeling, voorspelbare latencies of ruwe throughput—and richt je Lisp-implementatie zich op die behoeften?
Een andere eerlijke kritiek betreft ecosysteem-fit. Afhankelijk van het Lisp-dialect en het domein kunnen bibliotheken, tooling en de wervingspool kleiner zijn dan bij mainstream stacks. Dat kan zwaarder wegen dan elegantie als je snel met een breed team wilt leveren.
In plaats van Lisp te beoordelen op basis van stereotypen, evalueer je de onderliggende ideeën los: uniforme structuur, interactieve ontwikkeling en macros als instrument om domeinspecifieke abstracties te bouwen. Zelfs als je nooit een Lisp-systeem uitrolt, kunnen die concepten scherper maken hoe je over taalontwerp denkt—en hoe je code schrijft in elke taal.
McCarthy liet ons niet alleen een historische taal na—hij liet een set gewoonten achter die software makkelijker maken om te veranderen, uit te leggen en uit te breiden.
Geef de voorkeur aan eenvoudige kernen boven slimme oppervlakken. Een klein aantal orthogonale bouwstenen is makkelijker te leren en moeilijker kapot te krijgen.
Houd datastructuren uniform. Wanneer veel dingen dezelfde representatie delen (zoals lijsten/bomen), wordt tooling eenvoudiger: printers, debuggers, serializers en transformers zijn herbruikbaar.
Behandel programma’s als data (en data als programma’s) waar het helpt. Als je structuren kunt inspecteren en transformeren, kun je veiligere refactors, migraties en codegenerators bouwen.
Automatiseer saai werk. Garbage collection is het klassieke voorbeeld, maar het bredere punt is: investeer in automatisering die hele categorieën fouten voorkomt.
Optimaliseer voor feedback-lussen. Interactieve evaluatie (REPL-stijl) stimuleert kleine experimenten, snelle verificatie en beter begrip van gedrag.
Maak uitbreiding tot een eerste-klasse ontwerpsdoel. Lisp-macros zijn één antwoord; in andere ecosystemen kunnen dat plugins, templates, DSLs of compile-time transformaties zijn.
Voordat je een bibliotheek of architectuur kiest, neem een echt feature—zeg “kortingsregels” of “support ticket routing”—en teken het als een boom. Schrijf het daarna als geneste lijsten (of JSON). Vraag: wat zijn de knopen, wat zijn de bladeren, en welke transformaties heb je nodig?
Zelfs zonder Lisp kun je de mindset overnemen: bouw AST-achtige representaties, gebruik codegeneratie voor repetitieve glue en standaardiseer op data-eerst pijplijnen (parse → transform → evaluate). Veel teams behalen de voordelen simpelweg door tussenrepresentaties expliciet te maken.
Als je het REPL-principe wilt maar productfeatures levert in mainstream stacks, kun je de geest ook in tooling lenen: krappe iteratielussen, snapshots/rollback en expliciete planning voor uitvoering. Koder.ai, bijvoorbeeld, bevat een planningsmodus plus snapshots en rollback om snelle iteratie veiliger te maken—een operationele echo van Lisp’s “verander snel, maar behoud controle.”
McCarthy’s blijvende invloed is deze: programmeren wordt krachtiger wanneer we redeneren zelf programmeerbaar maken—en de weg van idee naar uitvoerbaar systeem zo direct mogelijk houden.
Symbolisch denken stelt concepten en relaties direct voor (bijv. “klant”, “is-een”, “hangt-af-van”, “als…dan…”), en past vervolgens regels en transformaties toe op die representaties.
Het is het meest bruikbaar wanneer je probleem vol zit met structuur, uitzonderingen en betekenis (regels-engines, planning, compilers, configuratie, workflow-logica), niet alleen rekenwerk.
McCarthy benadrukte dat redeneren als programma’s uit te drukken is — niet alleen berekeningen.
Dat perspectief heeft vormgegeven:
Lijsten zijn een minimale, flexibele manier om ‘dingen die uit delen bestaan’ te representeren. Omdat elementen zelf lijsten kunnen zijn, krijg je vanzelf boomstructuren.
Dat maakt het eenvoudig om:
S-expressies geven je één uniforme vorm voor code en data: geneste lijsten.
Die uniformiteit vereenvoudigt systemen omdat:
Een macro automatiseert herhalende code-structuren, niet alleen herhalende berekeningen.
Gebruik macros wanneer je wilt:
Als je alleen herbruikbare logica nodig hebt, is een functie meestal een betere keuze.
Garbage collection (GC) reclaimed automatisch geheugen dat niet meer bereikbaar is, waardoor hele categorieën bugs (dangling pointers, double frees) afnemen.
Het is vooral nuttig wanneer je programma veel kortlevende structuren creëert (lijsten/trees/ASTs), omdat je vrij kunt prototypen en refactoren zonder eerst een handmatig geheugen-eigendomsschema te ontwerpen.
Een REPL verkort de “denk → probeer → observeer”-lus. Je kunt een functie definiëren, uitvoeren, aanpassen en onmiddellijk opnieuw draaien.
Om hetzelfde voordeel in niet-Lisp-stacks te krijgen:
Related reading: /blog/the-repl-and-fast-feedback-loops
Veel moderne workflows gebruiken dezelfde onderliggende ideeën:
map/filter, compositie)Zelfs als je nooit Lisp gebruikt, gebruik je waarschijnlijk dagelijks van Lisp afgeleide .
Reële afwegingen zijn onder meer:
De praktische aanpak is: beoordeel de fit op basis van domein en beperkingen, niet op reputatie.
Probeer deze 10-minuten oefening:
Dat onthult vaak waar “code-als-data”-patronen, rules-engines of DSL-achtige configs je systeem kunnen vereenvoudigen.