Utforska hur John McCarthys symboliska angreppssätt och Lisp‑idéer—listor, rekursion och garbage collection—påverkade AI och modern programmering.

Det här är ingen museivisning av “gammal AI”. Det är en praktisk historielektion för alla som bygger mjukvara—programmerare, tech leads och produktbyggare—eftersom John McCarthys idéer formade hur vi tänker om vad programspråk är till för.
Lisp var inte bara en ny syntax. Det var ett vad om att mjukvara kunde manipulera idéer (inte bara tal) och att språkval kunde påskynda forskning, produktiteration och hela verktygsekosystem.
Ett användbart sätt att läsa McCarthys arv är som en fråga som fortfarande spelar roll idag: hur direkt kan vi förvandla avsikt till ett körbart system—utan att drunkna i boilerplate, friktion eller oavsiktlig komplexitet? Den frågan ekar från Lisps REPL ända fram till moderna “chat-till-app”-arbetsflöden.
John McCarthy är ihågkommen inte bara för att ha hjälpt till att starta AI som forskningsfält, utan för att han insisterade på en särskild typ av AI: system som kunde manipulera idéer, inte bara räkna fram svar. I mitten av 1950‑talet organiserade han Dartmouth Summer Research Project (där termen “artificiell intelligens” föreslogs) och påverkade AI-arbete vid MIT och senare Stanford. Men hans mest bestående bidrag kan vara den fråga han fortsatte driva: tänk om resonemang självt kunde uttryckas som ett program?
De tidiga datorsystemens framgångar var ofta numeriska: ballistiska tabeller, ingenjörssimuleringar, optimering och statistik. Dessa problem passade väl för aritmetik.
McCarthy siktade på något annat. Mänskligt resonemang arbetar ofta med begrepp som “om”, “eftersom”, “tillhör”, “är en typ av” och “alla saker som uppfyller dessa villkor”. Dessa representeras inte naturligt som flyttal.
McCarthys angreppssätt behandlade kunskap som symboler (namn, relationer, kategorier) och tänkte att tänkande är regelbaserade transformationer över dessa symboler.
Ett högnivå sätt att föreställa sig det: numeriska metoder svarar på “hur mycket?” medan symboliska metoder försöker svara “vad är det?” och “vad följer av vad vi vet?”.
När du tror att resonemang kan göras programmerbart behöver du ett språk som bekvämt kan representera uttryck som regler, logiska satser och nästlade relationer—och sedan bearbeta dem.
Lisp byggdes för att tjäna det målet. Istället för att tvinga idéer in i rigida, förbakade datastrukturer gjorde Lisp det naturligt att representera kod och kunskap i liknande form. Det valet var inte akademisk stil—det var en praktisk bro mellan att beskriva en tanke och att köra en procedur, vilket är just den bro McCarthy ville att AI skulle korsa.
När McCarthy och tidiga AI‑forskare sa “symboliskt” menade de inte mystisk matematik. En symbol är helt enkelt en meningsfull etikett: ett namn som customer, ett ord som hungry, eller en tagg som IF och THEN. Symboler är viktiga eftersom de låter ett program arbeta med idéer (kategorier, relationer, regler) istället för bara råa tal.
Ett enkelt sätt att föreställa sig det: kalkylark är fantastiska när din värld är kolumner och aritmetik. Symboliska system fungerar bra när din värld är regler, kategorier, undantag och struktur.
I många program är skillnaden mellan 42 och "age" inte datatypen—det är vad värdet står för. En symbol ger dig något du kan jämföra, lagra och kombinera utan att förlora betydelse.
Det gör det naturligt att representera saker som “Paris är en stad” eller “om batteriet är lågt, hitta en laddare.”
För att göra något användbart med symboler behöver du struktur. Lisp populariserade en mycket enkel struktur: listan. En lista är bara en ordnad grupp med element, och de elementen kan själva vara listor. Med den enidén kan du representera meningar, formulär och trädformad kunskap.
Här är ett litet konceptuellt exempel (visat i Lisp‑liknande stil):
(sentence (subject robot) (verb needs) (object power))
Det läser nästan som engelska: en sats bestående av subjekt, verb och objekt. Eftersom det är strukturerat kan ett program plocka ut (subject robot) eller ersätta (object power) med något annat.
När information ligger i symboliska strukturer blir klassiska AI‑uppgifter hanterbara:
Den viktiga förändringen är att programmet inte bara räknar; det manipulerar meningsfulla kunskapsbitar i en form som det kan inspektera och transformera.
Lisps designval stannade inte bara i akademin. De påverkade hur folk byggde verktyg och hur snabbt de kunde utforska idéer:
Dessa egenskaper tenderar att skapa ekosystem där experiment är billiga, prototyper blir produkter snabbare och team kan anpassa sig när krav förändras.
Lisp började med ett mycket praktiskt designproblem: hur skriver man program som kan arbeta med symboler lika naturligt som de arbetar med tal?
McCarthy försökte inte bygga en “bättre räknemaskin”. Han ville ha ett språk där ett uttryck som (is (parent Alice Bob)) kunde lagras, inspekteras, transformeras och resoneras om lika lätt som (+ 2 3).
Prioriteten var att göra symbolisk information enkel att representera och manipulera. Det ledde till fokus på listor och trädliknande strukturer, eftersom de passar väl för saker människor redan använder för att uttrycka mening: satser, logiska regler, nästlade kategorier och relationer.
Ett annat mål var att hålla språkkärnan liten och konsekvent. När ett språk har färre “specialfall” lägger du mindre tid på att memorera regler och mer tid på att komponera idéer. Lisp lutade mot en liten uppsättning byggstenar som kunde kombineras till större abstraktioner.
En nyckelinsikt var att program och data kan dela samma slags struktur. Enkelt uttryckt: om din data är en nästlad lista kan ditt program vara en nästlad lista också.
Det betyder att du kan:
Lisp populariserade också ett tankesätt: språk behöver inte vara one‑size‑fits‑all. De kan utformas kring ett problemområde—som resonemang, sökning och kunskapsrepresentation—och ändå påverka allmän programmering i årtionden.
S‑uttryck (förkortning av symbolic expressions) är Lisps signaturidé: ett enda, konsekvent sätt att representera kod och data som nästlade listor.
Vid en blick är ett S‑uttryck bara parenteser runt element—vissa element är atomer (som namn och tal), och vissa element är listor själva. Regeln “listor i listor” är hela poängen.
Eftersom strukturen är enhetlig byggs Lisp‑program från samma byggstenar hela vägen ner. Ett funktionsanrop, en konfigurationsliknande databit och en del av programstrukturen kan alla uttryckas som en lista.
Den konsekvensen ger omedelbar utdelning:
Även om du aldrig skriver Lisp är detta en viktig designlektion: när ett system byggs av en eller två förutsägbara former lägger du mindre tid på att slåss mot kantfall och mer tid på att bygga.
S‑uttryck uppmuntrar komposition eftersom små, läsbara delar naturligt kombineras till större. När ditt program är “bara nästlade listor” innebär kombination ofta att stoppa ett uttryck i ett annat eller bygga listor från återanvändbara bitar.
Det skjuter dig mot en modulär stil: skriv små operationer som gör en sak, och stapla dem för att uttrycka en större avsikt.
Den uppenbara nackdelen är ovanan. För många nybörjare ser parentesfylld syntax konstig ut.
Men uppsidan är förutsägbarhet: när du väl förstår nästlingsreglerna kan du pålitligt se strukturen i ett program—och verktyg kan också. Den klarheten är en stor anledning till att S‑uttryck fick effekter långt bortom Lisp självt.
Rekursion är lättast att förstå med en vardaglig metafor: städa ett stökigt rum genom att göra mindre “rum” av det. Du försöker inte lösa allt på en gång. Du plockar upp en sak, lägger den där den hör hemma, och upprepar samma handling på det som blir kvar. Stegen är enkla; kraften kommer från att upprepa dem tills inget finns kvar att göra.
Lisp lutar sig mot denna idé eftersom så mycket av dess data naturligt byggs av listor: en lista har en “första sak” och “resten”. Den formen passar rekursivt tänkande perfekt.
För att bearbeta en lista hanterar du första elementet, sedan applicerar du samma logik på resten av listan. När listan är tom slutar du—det är det rena “inget kvar att göra”-ögonblicket som gör rekursion begriplig istället för mystisk.
Föreställ dig att du vill ha totalen av en lista med tal.
Det är allt. Definitionen läser som enkelt språk, och programstrukturen speglar idén.
Symbolisk AI representerar ofta uttryck som trädstrukturer (en operator med underuttryck). Rekursion är ett naturligt sätt att “gå” det trädet: utvärdera vänster delen på samma sätt som höger delen, och fortsätt tills du når ett enkelt värde.
Dessa mönster formade senare funktionell programmering: små funktioner, tydliga basfall och datatransformationer som är lätta att resonera om. Även utanför Lisp leder vanan att bryta ned arbete i “gör ett steg, upprepa för resten” till renare program och färre dolda sidoeffekter.
Tidiga programmerare var ofta tvungna att hantera minne för hand: allokera utrymme, hålla reda på vem som “äger” det, och komma ihåg att frigöra det vid rätt tidpunkt. Det arbetet saktar inte bara ner utveckling—det skapar en särskild klass av buggar som är svåra att reproducera och lätta att släppa: läckor som tyst försämrar prestanda, och hängande pekare som kraschar ett program långt efter det ursprungliga misstaget.
John McCarthy introducerade garbage collection för Lisp som ett sätt att låta programmerare fokusera på mening snarare än bokföring.
På en hög nivå hittar GC automatiskt minnesbitar som inte längre är nåbara från det körande programmet—värden som inget någonsin kan använda igen—och återvinner det utrymmet.
Istället för att fråga “frigjorde vi varje objekt exakt en gång?” skiftar GC frågan till “är det här objektet fortfarande åtkomligt?”. Om programmet inte kan nå det betraktas det som skräp.
För symbolisk AI skapade Lisp‑program ofta många kortlivade listor, träd och mellanresultat. Manuell minneshantering skulle göra experimenterande till en ständig kamp mot resurshantering.
GC förändrar dag‑till‑dag‑upplevelsen:
Nyckelidén är att ett språks funktion kan vara en teammultiplikator: färre timmar på att debugga mystisk korruption betyder mer tid till att förbättra själva logiken.
McCarthys val stannade inte i Lisp. Många senare system antog GC (och variationer av det) eftersom avvägningen ofta lönar sig: Java, C#, Python, JavaScript‑runtider och Go lutar alla mot garbage collection för att göra storskalig utveckling säkrare och snabbare—even när prestanda är en prioritet.
I Lisp är ett uttryck en kodbit skriven i en konsekvent form (ofta en lista). Utvärdering är helt enkelt processen att avgöra vad det uttrycket betyder och vad det producerar.
Till exempel, när du skriver något som “lägg ihop dessa tal” eller “anropa den här funktionen med dessa indata”, följer evaluatorn ett litet antal regler för att omvandla uttrycket till ett resultat. Tänk på det som språkmatchens domare: den avgör vad som ska göras härnäst, i vilken ordning och när man ska sluta.
McCarthys nyckelgrepp var inte bara att uppfinna ny syntax—det var att hålla “betydelsemotorn” kompakt och regelbunden. När evaluatorn byggs av några klara regler händer två goda saker:
Denna konsekvens är en anledning till att Lisp blev en lekplats för idéer inom symbolisk AI: forskare kunde testa nya representationer och kontrollstrukturer snabbt, utan att vänta på att kompilatorlagen skulle designa om språket.
Makron är Lisps sätt att låta dig automatisera repetitiva kodformer, inte bara repetitiva värden. Där en funktion hjälper dig att undvika upprepade beräkningar hjälper en makro dig att undvika upprepade strukturer—vanliga mönster som “gör X, men logga det också”, eller “definiera ett mini‑språk för regler.”
Den praktiska effekten är att Lisp kan växa nya bekvämligheter inifrån. Många moderna verktyg ekar denna idé—mallssystem, kodgeneratorer och metaprogrammeringsfunktioner—eftersom de stöder samma mål: snabbare experimenterande och tydligare avsikter.
Om du är nyfiken på hur detta tanke‑sätt påverkade vardagliga utvecklingsarbetsflöden, se /blog/the-repl-and-fast-feedback-loops.
En stor del av Lisps attraktionskraft var inte bara språket—det var hur du arbetade med det. Lisp populariserade REPL: Read–Eval–Print Loop. I vardagliga termer är det som att ha en konversation med datorn. Du skriver ett uttryck, systemet kör det omedelbart, skriver ut resultatet och väntar på nästa inmatning.
Istället för att skriva ett helt program, kompilera det, köra det och sedan jaga vad som gick fel, kan du pröva idéer ett litet steg i taget. Du kan definiera en funktion, testa den med några indata, justera den och testa igen—allting på sekunder.
Den rytmen uppmuntrar experimenterande, vilket var viktigt för tidig AI‑forskning där man ofta inte visste rätt angreppssätt från början.
Snabb återkoppling förvandlar “stora satsningar” till “små kontroller”. För forskning gör det enklare att utforska hypoteser och inspektera mellanresultat.
För produktprototyping minskar det kostnaden för iteration: du kan verifiera beteende med verkliga data snabbt, upptäcka kantfall tidigare och förfina funktioner utan att vänta på långa byggcykler.
Det är också därför moderna vibe‑coding‑verktyg känns lockande: de är i princip en aggressiv kompression av återkopplingsloopen. Till exempel använder Koder.ai ett chattgränssnitt (med en agentbaserad arkitektur under huven) för att förvandla produktavsikt till fungerande webb-, backend‑ eller mobilkod snabbt—ofta görandes “pröva → justera → pröva igen”-loopen mer lik en REPL än en traditionell pipeline.
REPL‑idén återfinns idag i:
Olika verktyg, samma princip: förkorta avståndet mellan tänkande och att se resultat.
Team drar mest nytta av REPL‑liknande arbetsflöden när de utforskar osäkra krav, bygger data‑tunga funktioner, designar API:er eller debuggar knepig logik. Om arbetet handlar om snabb lärning—om användare, data eller kantfall—är interaktiv återkoppling inte en lyx; det är en multiplikator.
Lisp “vann” inte genom att bli allas dagliga syntax. Det vann genom att så idéer som tyst blev norm i många ekosystem.
Koncept som Lisp behandlade som standard—funktioner som värden, omfattande användning av högreordningsoperationer och en preferens för att bygga program genom att komponera små delar—visar sig nu i många språk. Även språk som inte liknar Lisp uppmuntrar map/filter‑stilar, immutabla data‑vanor och rekursionsliknande tänkande (ofta uttryckt med iteratorer eller fold‑operationer).
Nyckelshiftset är mentalt: behandla datatransformationer som pipelines och beteende som något du kan skicka runt.
Lisp gjorde program enkla att representera som data. Det mindsetet syns idag i hur vi bygger och manipulerar ASTs (abstract syntax trees) för kompilatorer, formatterare, linters och codegen. När du arbetar med en AST gör du en nära släkting till “kod som data”, även om strukturerna är JSON‑objekt, typade noder eller bytecode‑grafer.
Samma symboliska angreppssätt driver praktisk automation: konfigurationsformat, mallningssystem och byggpipelines förlitar sig alla på strukturerade representationer som verktyg kan inspektera, transformera och validera.
Moderna Lisp‑familjer (i vid bemärkelse: samtida Lisps och Lisp‑inspirerade verktyg) fortsätter påverka hur team designar interna DSL:er—små, fokuserade mini‑språk för tester, distribution, datarengöring eller UI.
Utanför Lisp strävar makrosystem, metaprogrammeringsbibliotek och codegen‑ramverk efter samma resultat: utöka språket för att passa problemet.
En pragmatisk slutsats: syntaxpreferenser förändras, men hållbara idéer—symbolisk struktur, komponerbara funktioner och utbyggbarhet—fortsätter betala hyra över årtionden och kodbaser.
Lisp har ett rykte som pendlar mellan “briljant” och “oläsbart”, ofta baserat på andrahandsintryck snarare än vardagserfarenhet. Sanningen är mera ordinär: Lisp gör vissa val som är kraftfulla i rätt sammanhang och besvärliga i andra.
För nykomlingar kan Lisps enhetliga syntax kännas som att titta på programmets “insida” snarare än en polerad yta. Den obehagskänslan är verklig, särskilt om du är van vid språk där syntaxen visuellt skiljer konstruktioner åt.
Historiskt sett är Lisps struktur också poängen: kod och data delar samma form, vilket gör program enklare att transformera, generera och analysera. Med bra editorstöd (indentering, strukturell navigation) läses Lisp‑kod ofta mer efter form än efter att räkna parenteser.
En vanlig stereotyph är att Lisp är inneboende långsamt. Historiskt hade vissa implementationer svårt jämfört med lågnivåspråk, och dynamiska egenskaper kan ge overhead.
Men det är inte korrekt att betrakta “Lisp” som en enda prestandaprofil. Många Lisp‑system har länge stöd för kompilering, typdeklarationer och seriös optimering. Ett mer användbart perspektiv är: hur mycket kontroll behöver du över minneslayout, förutsägbar latens eller rå genomströmning—och riktar din Lisp‑implementation in sig på de behoven?
En annan rättvis kritik är ekosystempassform. Beroende på Lisp‑dialekt och domän kan bibliotek, verktyg och rekryteringspool vara mindre än i mainstream‑stackar. Det kan spela större roll än språkelegans om du snabbt ska leverera med ett brett team.
Istället för att döma Lisp efter dess stereotyper, utvärdera dess underliggande idéer själv: enhetlig struktur, interaktiv utveckling och makron som verktyg för att bygga domän‑specifika abstraktioner. Även om du aldrig levererar ett Lisp‑system kan dessa koncept skärpa hur du tänker om språkutformning—och hur du skriver kod i vilket språk som helst.
McCarthy lämnade oss inte bara ett historiskt språk—han lämnade en uppsättning vanor som fortfarande gör mjukvara enklare att förändra, förklara och utöka.
Innan du väljer ett bibliotek eller en arkitektur, ta en verklig funktion—säg “rabattregler” eller “supportticket‑routing”—och rita den som ett träd. Skriv sedan om det trädet som nästlade listor (eller JSON). Fråga: vilka är noderna, vilka är bladen, och vilka transformationer behöver du?
Även utan Lisp kan du anta tankesättet: bygg AST‑liknande representationer, använd kodgenerering för repeterande lim, och standardisera på data‑först pipelines (parse → transform → evaluate). Många team får fördelarna genom att göra mellanrepresentationer explicita.
Om du gillar REPL‑principen men levererar funktioner i mainstream‑stackar kan du också låna andan i verktygen: tajta iterationsloopar, snapshots/rollback och tydlig planering före exekvering. Koder.ai, till exempel, inkluderar ett planeringsläge plus snapshots och rollback för att hålla snabb iteration säkrare—en operationell ekko av Lisps “ändra snabbt, men håll kontrollen”.
McCarthys bestående inflytande är detta: programmering blir kraftfullare när vi gör resonemang självt programmerbart—och håller vägen från idé till körbart system så direkt som möjligt.
Symboliskt tänkande representerar begrepp och relationer direkt (t.ex. “kund”, “är-en”, “beroende-av”, “om…så…”), och tillämpar sedan regler och transformationer på de representationerna.
Det är mest användbart när problemet är fullt av struktur, undantag och mening (regelmotorer, planering, kompilatorer, konfiguration, arbetsflödeslogik), inte bara aritmetik.
McCarthy drev idén att resonemang kan uttryckas som program—inte bara beräkning.
Det perspektivet formade:
Listor är ett minimalt, flexibelt sätt att representera “saker som är gjorda av delar”. Eftersom listelement själva kan vara listor får du naturligt trädstrukturer.
Det gör det enkelt att:
S-uttryck ger dig en enhetlig form för kod och data: nästlade listor.
Denna enhetlighet gör system enklare eftersom:
En makro automatiserar repetitiva kodstrukturer, inte bara repetitiva beräkningar.
Använd makron när du vill:
Om du bara behöver återanvändbar logik är en funktion vanligtvis ett bättre val.
Garbage collection (GC) återvinner automatiskt minne som inte längre är nåbart, vilket minskar hela kategorier av fel (hängande pekare, dubbel-fri).
Det är särskilt hjälpsamt när ditt program skapar många kortlivade strukturer (listor/träd/ASTs), eftersom du kan prototypa och refaktorera utan att först designa ett manuellt minnesägande.
En REPL förkortar loopen “tänk → pröva → observera”. Du kan definiera en funktion, köra den, justera den och köra igen direkt.
För att få samma fördel i icke-Lisp-stackar:
Relaterad läsning: /blog/the-repl-and-fast-feedback-loops
Många moderna arbetsflöden återanvänder samma underliggande idéer:
map/filter, komposition)Även om du aldrig levererar med Lisp använder du sannolikt Lisp-efterkommande vanor dagligen.
Vanliga kompromisser inkluderar:
Det praktiska tillvägagångssättet är att utvärdera passform efter domän och begränsningar, inte rykte.
Prova denna 10-minutersövning:
Detta avslöjar ofta var “kod-som-data”-mönster, regelmotorer eller DSL-liknande konfigurationer förenklar systemet.