John McCarthy, Lisp e le radici della progettazione dell'IA simbolica
Esplora come l'approccio simbolico di John McCarthy e le idee di Lisp—liste, ricorsione e garbage collection—hanno influenzato l'AI e la programmazione moderna.

Perché McCarthy e Lisp contano ancora
Questa non è una visita al museo dell’“AI vecchia scuola”. È una lezione di storia pratica per chi costruisce software—programmatori, tech lead e product builder—perché le idee di John McCarthy hanno plasmato il modo in cui pensiamo a cosa servono i linguaggi di programmazione.
Lisp non era solo una nuova sintassi. Era una scommessa: il software poteva manipolare idee (non solo numeri) e le scelte di progettazione del linguaggio potevano accelerare ricerca, iterazione di prodotto e interi ecosistemi di tool.
Un modo utile per leggere l’eredità di McCarthy è come una domanda ancora rilevante oggi: quanto direttamente possiamo trasformare l’intento in un sistema eseguibile—senza affogarci in boilerplate, attrito o complessità accidentale? Quella domanda riecheggia dal REPL di Lisp fino ai moderni workflow “chat-to-app”.
La grande idea di John McCarthy: rendere il ragionamento programmabile
John McCarthy è ricordato non solo per aver contribuito a lanciare l’AI come campo di ricerca, ma per aver insistito su un tipo specifico di AI: sistemi che potessero manipolare idee, non solo calcolare risposte. A metà degli anni ’50 organizzò il Dartmouth Summer Research Project (dove fu proposto il termine “artificial intelligence”) e poi influenzò il lavoro sull’AI al MIT e successivamente a Stanford. Ma il suo contributo più duraturo potrebbe essere la domanda che continuava a porre: e se il ragionamento stesso potesse essere espresso come un programma?
Dai numeri ai simboli
I primi successi del calcolo erano per lo più numerici: tabelle balistiche, simulazioni ingegneristiche, ottimizzazione e statistica. Questi problemi si prestavano bene all’aritmetica.
McCarthy mirava a qualcosa di diverso. Il ragionamento umano spesso lavora con concetti come “se”, “perché”, “appartiene a”, “è un tipo di” e “tutte le cose che soddisfano queste condizioni”. Queste non si rappresentano naturalmente con valori floating-point.
L’approccio di McCarthy trattava la conoscenza come simboli (nomi, relazioni, categorie) e considerava il pensiero come trasformazioni basate su regole su quei simboli.
In alto livello: gli approcci numerici rispondono a “quanto?” mentre gli approcci simbolici cercano di rispondere a “cos’è?” e “cosa segue da ciò che sappiamo?”.
Lisp come strumento del mestiere
Se credi che il ragionamento possa essere reso programmabile, ti serve un linguaggio che rappresenti comodamente espressioni come regole, affermazioni logiche e relazioni annidate—e poi le processi.
Lisp fu costruito per questo scopo. Invece di forzare le idee in strutture dati rigide e prefatte, Lisp rendeva naturale rappresentare codice e conoscenza in forma simile. Questa scelta non era di stile accademico: era un ponte pratico tra descrivere un pensiero ed eseguire una procedura, proprio il ponte che McCarthy voleva che l’AI attraversasse.
Pensiero simbolico, in parole semplici
Quando McCarthy e i primi ricercatori di AI dicevano “simbolico”, non intendevano matematica misteriosa. Un simbolo è semplicemente un'etichetta significativa: un nome come customer, una parola come hungry o un tag come IF e THEN. I simboli contano perché permettono a un programma di lavorare con idee (categorie, relazioni, regole) anziché solo numeri grezzi.
Un modo semplice per immaginarlo: i fogli di calcolo sono ottimi quando il tuo mondo è colonne e aritmetica. I sistemi simbolici sono ottimi quando il tuo mondo è fatto di regole, categorie, eccezioni e struttura.
Simboli: nomi con intenzione
In molti programmi, la differenza tra 42 e "age" non è il tipo di dato—è ciò che il valore rappresenta. Un simbolo ti dà qualcosa da confrontare, memorizzare e combinare senza perdere significato.
Questo rende naturale rappresentare affermazioni come “Parigi è una città” o “se la batteria è scarica, trova un caricabatterie”.
Liste: la struttura più semplice per “fatto di parti”
Per fare qualcosa di utile con i simboli servono strutture. Lisp ha reso popolare una struttura molto semplice: la lista. Una lista è solo un gruppo ordinato di elementi, e quegli elementi possono essere a loro volta liste. Con quell’idea puoi rappresentare frasi, forme e conoscenza ad albero.
Ecco un piccolo esempio concettuale (in stile Lisp):
(sentence (subject robot) (verb needs) (object power))
Si legge quasi come l’inglese: una frase composta da soggetto, verbo e oggetto. Essendo strutturata, un programma può estrarre (subject robot) o sostituire (object power) con qualcos’altro.
Dalle strutture al ragionamento: regole, pianificazione, ricerca
Quando l’informazione è in strutture simboliche, i classici compiti di AI diventano affrontabili:
- Regole: IF una pattern corrisponde, THEN concludi qualcosa di nuovo.
- Pianificazione: rappresenta obiettivi e azioni come liste, poi concatena azioni per raggiungere un obiettivo.
- Ricerca: esplora diverse combinazioni simboliche possibili (stati) per trovare una soluzione.
La svolta è che il programma non si limita a calcolare; manipola pezzi di conoscenza significativi in una forma che può ispezionare e trasformare.
Perché le scelte di progettazione del linguaggio riecheggiano per decenni
Le decisioni progettuali di Lisp non rimasero solo in ambito accademico. Hanno influenzato come si costruiscono gli strumenti e la velocità con cui si esplorano le idee:
- Espressività: Lisp rendeva naturale rappresentare conoscenza strutturata e trasformarla.
- Iterazione rapida: lo sviluppo interattivo accorciava il ciclo tra “pensare” e “provare”.
- Estendibilità: il linguaggio incoraggiava la costruzione di nuove astrazioni invece di ripetere pattern.
Queste caratteristiche danno origine a ecosistemi dove sperimentare è poco costoso, i prototipi diventano prodotti più in fretta e i team si adattano quando i requisiti cambiano.
Gli obiettivi di progettazione dietro Lisp
Lisp nacque da un problema pratico: come scrivere programmi che possano lavorare con simboli con la stessa naturalezza con cui lavorano con i numeri?
McCarthy non voleva costruire un “calcolatore migliore”. Voleva un linguaggio dove un’espressione come (is (parent Alice Bob)) potesse essere memorizzata, ispezionata, trasformata e ragionata con la stessa facilità di (+ 2 3).
Un linguaggio pensato per dati simbolici
La priorità era rendere facile rappresentare e manipolare informazioni simboliche. Questo portò a concentrarsi su liste e strutture ad albero, perché si mappano bene a ciò che gli umani usano per esprimere significato: frasi, regole logiche, categorie nidificate e relazioni.
Semplicità che sblocca flessibilità
Un altro obiettivo era mantenere il nucleo del linguaggio piccolo e coerente. Quando un linguaggio ha meno “casi speciali”, passi meno tempo a memorizzare regole e più tempo a comporre idee. Lisp puntava su un piccolo set di mattoni fondamentali combinabili in astrazioni più grandi.
Programmi che sembrano dati
Un’intuizione chiave fu che programmi e dati possono condividere la stessa struttura. In parole povere: se i tuoi dati sono liste annidate, anche il tuo programma può essere una lista annidata.
Questo significa che puoi:
- generare un programma (come dato strutturato)
- modificarlo (come qualsiasi altro dato)
- poi eseguirne il risultato
Un cambio culturale nella progettazione dei linguaggi
Lisp ha anche diffuso una mentalità: i linguaggi non devono essere monouso. Possono essere progettati attorno a un dominio—come ragionamento, ricerca e rappresentazione della conoscenza—e influenzare comunque la programmazione general-purpose per decenni.
S-expressions: una struttura semplice con grandi conseguenze
Le S-expressions (abbreviazione di symbolic expressions) sono l’idea distintiva di Lisp: un modo singolo e coerente per rappresentare codice e dati come liste annidate.
A prima vista, una S-expression è solo parentesi intorno a elementi—alcuni elementi sono atomi (nomi e numeri), altri sono a loro volta liste. La regola “liste dentro liste” è tutto il punto.
Una forma per tutto
Poiché la struttura è uniforme, i programmi Lisp sono costruiti con gli stessi mattoni a tutti i livelli. Una chiamata di funzione, un pezzo di dati simile a una configurazione e un costrutto del programma possono tutti essere espressi come lista.
Questa coerenza paga subito:
- Il parsing è più semplice: il linguaggio non ha bisogno di uno zoo di casi speciali.
- Le trasformazioni del codice diventano pratiche: è più facile camminare un albero di liste e riscriverne parti.
- La valutazione è più regolare: l’evaluatore può trattare molti costrutti in modo coerente, perché condividono la stessa forma sottostante.
Anche se non scrivi mai Lisp, questa è una lezione importante: quando un sistema è costruito da una o due forme prevedibili, passi meno tempo a combattere casi limite e più tempo a costruire.
Composizione che si percepisce
Le S-expressions incoraggiano la composizione perché pezzi piccoli e leggibili si combinano naturalmente in altri più grandi. Quando il tuo programma è “solo liste annidate”, combinare idee spesso significa nidificare un’espressione dentro un’altra o assemblare liste da parti riusabili.
Questo ti spinge verso uno stile modulare: scrivi operazioni piccole che fanno una cosa, poi le incastri per esprimere un intento più ampio.
Il compromesso: le parentesi
Lo svantaggio ovvio è la stranezza per i neofiti. Per molti la sintassi piena di parentesi sembra insolita.
Ma il vantaggio è la prevedibilità: una volta capite le regole di nidificazione, puoi vedere in modo affidabile la struttura di un programma—e anche gli strumenti possono farlo. Questa chiarezza è una grande ragione per cui le S-expressions hanno avuto conseguenze ben oltre Lisp.
Ricorsione e processamento di liste come mattoni fondamentali
La ricorsione si capisce facilmente con una metafora quotidiana: pulire una stanza disordinata facendo stanze più piccole. Non tenti di risolvere tutto in una volta. Prendi un oggetto, lo metti dove deve stare, poi ripeti la stessa azione su ciò che rimane. I passi sono semplici; la forza viene dalla ripetizione fino a quando non resta più nulla da fare.
Lisp punta su questa idea perché gran parte dei suoi dati è naturalmente costruita da liste: una lista ha una “prima cosa” e “il resto”. Quella forma si adatta perfettamente al pensiero ricorsivo.
Per processare una lista, gestisci il primo elemento e poi applichi la stessa logica al resto. Quando la lista è vuota, ti fermi—questo è il momento “non c’è più nulla da fare” che rende la ricorsione ben definita anziché misteriosa.
Mini-esempio concettuale: sommare una lista
Immagina di voler ottenere il totale di una lista di numeri.
- Se la lista è vuota, la somma è 0.
- Altrimenti, la somma è il primo numero più la somma del resto.
Questo è tutto. La definizione si legge come l’inglese, e la struttura del programma rispecchia l’idea.
Pensare come “camminare un albero”
L’AI simbolica spesso rappresenta espressioni come strutture ad albero (un operatore con sotto-espressioni). La ricorsione è un modo naturale per “camminare” quell’albero: valuta la parte sinistra nello stesso modo in cui valuti la destra e continua finché non raggiungi un valore semplice.
Questi pattern hanno contribuito a plasmare la programmazione funzionale successiva: funzioni piccole, casi base chiari e trasformazioni di dati facili da ragionare. Anche fuori da Lisp, l’abitudine di spezzare il lavoro in “fai un passo, poi ripeti sul resto” porta a programmi più puliti e con meno effetti collaterali nascosti.
Garbage collection: una caratteristica di produttività, non solo un dettaglio
I primi programmatori spesso dovevano gestire la memoria manualmente: allocare spazio, tracciare chi “possiede” l’oggetto e ricordarsi di liberarlo al momento giusto. Questo lavoro non rallenta solo lo sviluppo: genera una classe di bug difficili da riprodurre e facili da spedire: perdite che degradano le prestazioni e puntatori pendenti che fanno crashare il programma molto dopo l’errore originale.
Cosa fa la garbage collection (in termini semplici)
John McCarthy introdusse la garbage collection per Lisp come modo per permettere ai programmatori di concentrarsi sul significato invece che sulla contabilità.
A grandi linee, la garbage collection (GC) trova automaticamente pezzi di memoria non più raggiungibili dal programma in esecuzione—valori che nulla potrà più usare—e recupera quello spazio.
Invece di chiederti “abbiamo liberato ogni oggetto esattamente una volta?”, la GC sposta la domanda su “questo oggetto è ancora accessibile?”. Se il programma non può raggiungerlo, è considerato spazzatura.
Perché questo è davvero produttività
Per il lavoro di AI simbolica, i programmi Lisp creano frequentemente molte liste, alberi e risultati intermedi a vita breve. La gestione manuale della memoria trasformerebbe la sperimentazione in una battaglia costante con la pulizia delle risorse.
La GC cambia l’esperienza quotidiana:
- Iterazione più veloce: puoi prototipare liberamente senza progettare prima una strategia di memoria.
- Meno errori da crash: intere categorie di guasti legati ai puntatori vengono ridotte o eliminate.
- Codice più pulito: le funzioni possono restituire nuove strutture senza contratti nascosti su chi deve liberarle.
L’idea chiave è che una caratteristica del linguaggio può essere un moltiplicatore per il team: meno ore spese a debug di corruzione misteriosa significa più tempo per migliorare la logica.
Influenza a lungo termine
La scelta di McCarthy non rimase confinata a Lisp. Molti sistemi successivi adottarono la GC (e sue varianti) perché il compromesso spesso ripaga: Java, C#, Python, runtime JavaScript e Go si affidano alla garbage collection per rendere lo sviluppo su larga scala più sicuro e veloce—anche quando le prestazioni sono importanti.
Valutazione e macro: estendere il linguaggio dall'interno
Cosa significa “valutazione” (senza gergo)
In Lisp, un espressione è un pezzo di codice scritto in una forma coerente (spesso una lista). La valutazione è semplicemente il processo di decidere cosa quell’espressione significa e cosa produce.
Per esempio, quando scrivi qualcosa come “somma questi numeri” o “chiama questa funzione con questi input”, l’evaluatore segue un piccolo set di regole per trasformare quell’espressione in un risultato. Pensalo come l’arbitro del linguaggio: decide cosa fare dopo, in quale ordine e quando fermarsi.
Perché un evaluatore piccolo e coerente è una superpotenza
La mossa chiave di McCarthy non fu solo inventare una nuova sintassi—fu mantenere il “motore di significato” compatto e regolare. Quando l’evaluatore è costruito su poche regole chiare, succedono due cose buone:
- Il linguaggio è più facile da razionalizzare, perché non devi gestire dozzine di casi speciali.
- Puoi estendere il linguaggio senza riscrivere l’intero sistema, perché nuove funzionalità possono essere espresse in termini di quelle esistenti.
Questa coerenza è una delle ragioni per cui Lisp divenne un campo di prova per idee nell’AI simbolica: i ricercatori potevano provare nuove rappresentazioni e controlli rapidamente, senza aspettare che un team di compilazione riprogettasse il linguaggio.
Macro: automazione per schemi di codice
Le macro sono il modo di Lisp per automatizzare forme di codice ripetitive, non solo valori ripetuti. Dove una funzione ti aiuta a evitare di ripetere calcoli, una macro ti aiuta a evitare di ripetere strutture comuni—pattern come “fai X, ma registra anche”, o “definisci una mini-lingua per regole”.
L’effetto pratico è che Lisp può far crescere nuove comodità dall’interno. Molti strumenti moderni richiamano quest’idea—sistemi di template, generatori di codice e funzionalità di metaprogrammazione—perché supportano lo stesso obiettivo: sperimentazione più rapida e intenzione più chiara.
Se ti interessa come questa mentalità ha influenzato i workflow quotidiani, vedi /blog/the-repl-and-fast-feedback-loops.
Il REPL e i cicli di feedback rapidi
Una grande parte dell’attrattiva di Lisp non era solo il linguaggio—era il modo in cui ci lavoravi. Lisp diffuse il REPL: Read–Eval–Print Loop. In termini pratici, è come avere una conversazione con il computer. Scrivi un’espressione, il sistema la esegue immediatamente, stampa il risultato e aspetta il tuo prossimo input.
Il flusso di lavoro in parole semplici
Invece di scrivere un intero programma, compilarlo, eseguirlo e poi cercare cosa è andato storto, puoi provare idee un piccolo passo alla volta. Definisci una funzione, testala con pochi input, aggiustala e ritestala—tutto in pochi secondi.
Queto ritmo incoraggia la sperimentazione, cosa cruciale nell’AI primitiva dove spesso non si conosceva l’approccio giusto a priori.
Perché i cicli di feedback stretti cambiano i risultati
Il feedback rapido trasforma “scommesse grandi” in “controlli piccoli”. Per la ricerca, rende più facile esplorare ipotesi e ispezionare risultati intermedi.
Per il prototyping di prodotto, riduce il costo dell’iterazione: puoi validare il comportamento con dati reali rapidamente, notare casi limite prima e affinare le funzionalità senza lunghe build.
Questo è anche il motivo per cui gli strumenti vibe-coding moderni sono interessanti: comprimono i cicli di feedback. Per esempio, Koder.ai usa un’interfaccia chat (con un’architettura agent-based sotto il cofano) per trasformare l’intento di prodotto in codice web, backend o mobile funzionante—spesso facendo sembrare il ciclo “prova → aggiusta → riprova” più vicino a un REPL che a una pipeline tradizionale.
Equivalenti moderni che già conosci
L’idea del REPL si ritrova oggi in:
- shell interattive Python/Node
- Jupyter e altri notebook
- console degli strumenti di sviluppo del browser
- “live reload” e hot module replacement nelle app web
Strumenti diversi, stesso principio: accorciare la distanza tra pensare e vedere.
Quando lo sviluppo interattivo è più utile
I team traggono più beneficio dai workflow tipo REPL quando esplorano requisiti incerti, costruiscono funzionalità data-heavy, progettano API o debugano logiche complesse. Se il lavoro implica apprendimento rapido—su utenti, dati o casi limite—lo sviluppo interattivo non è un lusso; è un moltiplicatore.
Come le idee di Lisp si sono diffuse nella programmazione
Lisp non ha “vinto” diventando la sintassi quotidiana di tutti. Ha vinto seminando idee che sono diventate normali in molti ecosistemi.
Pattern funzionali nel codice quotidiano
Concetti che Lisp considerava predefiniti—funzioni come valori, uso estensivo di operazioni higher-order e preferenza per costruire programmi componendo piccole parti—appaiono ormai ovunque. Anche in linguaggi che non somigliano a Lisp si incoraggiano trasformazioni map/filter, abitudini di dati immutabili e pensiero ricorsivo (spesso espresso con iteratori o fold).
Lo spostamento mentale è: tratta le trasformazioni di dati come pipeline e il comportamento come qualcosa che puoi passare in giro.
Rappresentazioni simboliche: dall’AI a compilatori e tool
Lisp rese i programmi facili da rappresentare come dati. Quella mentalità si vede oggi nel modo in cui costruiamo e manipoliamo AST (abstract syntax trees) per compilatori, formatter e generatori di codice. Quando lavori con un AST stai facendo una stretta cugina di “codice come dati”, anche se le strutture sono oggetti JSON, nodi tipizzati o grafi di bytecode.
Lo stesso approccio simbolico alimenta l’automazione pratica: formati di configurazione, sistemi di template e pipeline di build si basano su rappresentazioni strutturate che gli strumenti possono ispezionare, trasformare e validare.
DSL ed estendibilità senza riscrivere tutto
I linguaggi della famiglia Lisp (in senso ampio: Lisp contemporanei e strumenti ispirati a Lisp) continuano a influenzare il design di DSL interni—mini-linguaggi focalizzati per test, deployment, data wrangling o UI.
Fuori da Lisp, sistemi di macro, librerie di metaprogrammazione e framework di codegen mirano allo stesso risultato: estendere il linguaggio per adattarlo al problema.
Una lezione pragmatica: le preferenze di sintassi cambiano, ma le idee durevoli—struttura simbolica, funzioni componibili ed estendibilità—continuano a dare valore attraverso decenni e codebase.
Compromessi e idee sbagliate su Lisp
Lisp ha una reputazione che oscilla tra “geniale” e “illeggibile”, spesso basata su impressioni di seconda mano anziché sull’esperienza quotidiana. La verità è più ordinaria: Lisp prende decisioni che sono potenti nel contesto giusto e scomode in altri.
“Troppe parentesi” (e la questione della leggibilità)
Per i nuovi arrivati, la sintassi uniforme di Lisp può sembrare di guardare l’“interno” di un programma più che la sua superficie levigata. Questa sensazione è reale, specialmente se sei abituato a linguaggi in cui la sintassi separa visivamente costrutti diversi.
Storicamente, però, la struttura di Lisp è anche il punto: codice e dati condividono la stessa forma, il che rende i programmi più facili da trasformare, generare e analizzare. Con un buon supporto dell’editor (indentazione, navigazione strutturale), il codice Lisp si legge spesso più per forma che contando parentesi.
Prestazioni: mito, storia e realtà attuale
Uno stereotipo comune è che Lisp sia intrinsecamente lento. Storicamente, alcune implementazioni hanno sofferto rispetto a linguaggi di basso livello, e le caratteristiche dinamiche possono aggiungere overhead.
Ma non è corretto trattare “Lisp” come un profilo di prestazioni unico. Molti sistemi Lisp supportano compilazione, dichiarazioni di tipo e ottimizzazioni serie. Il quadro più utile è: quanto controllo ti serve sul layout della memoria, sulla latenza prevedibile o sul throughput grezzo—e la tua implementazione Lisp mira a questi obiettivi?
Ecosistema e recruiting: vincoli pratici, non fallimenti morali
Un’altra critica ragionevole è l’adeguatezza dell’ecosistema. A seconda del dialetto Lisp e del dominio, librerie, tooling e pool di talenti possono essere più piccoli rispetto agli stack mainstream. Questo può pesare più dell’eleganza del linguaggio se devi spedire rapidamente con un team ampio.
Una conclusione equilibrata
Invece di giudicare Lisp dalle sue stereotipie, valuta le sue idee di base: struttura uniforme, sviluppo interattivo e macro come strumento per costruire astrazioni di dominio. Anche se non distribuisci mai un sistema Lisp, quei concetti possono affinare il modo in cui pensi al design dei linguaggi e a come scrivi codice in qualsiasi ambiente.
Spunti pratici per progettazione del linguaggio e coding quotidiano
McCarthy non ci ha lasciato solo un linguaggio storico—ci ha lasciato un insieme di abitudini che rendono ancora il software più facile da cambiare, spiegare ed estendere.
6 lezioni da rubare
-
Preferisci nuclei semplici a superfici ingegnose. Un piccolo set di mattoni ortogonali è più facile da imparare e più difficile da rompere.
-
Mantieni uniformi le forme dei dati. Quando molte cose condividono la stessa rappresentazione (liste/alberi), gli strumenti diventano più semplici: printer, debugger, serializer e transformer possono essere riutilizzati.
-
Tratta i programmi come dati (e i dati come programmi) quando aiuta. Se puoi ispezionare e trasformare strutture, puoi realizzare refactor più sicuri, migrazioni e generatori di codice.
-
Automatizza il lavoro noioso. La garbage collection è l’esempio classico, ma il punto più ampio è: investi in automazioni che prevengano intere classi di errori.
-
Ottimizza per i cicli di feedback. La valutazione interattiva (stile REPL) incoraggia piccoli esperimenti, verifica rapida e migliore intuizione sul comportamento.
-
Fai dell’estendibilità un obiettivo primario. Le macro di Lisp sono una risposta; in altri ecosistemi questo può essere plugin, template, DSL o trasformazioni a compile-time.
Un piccolo esercizio (10 minuti)
Prima di scegliere una libreria o un’architettura, prendi una funzionalità reale—per esempio “regole sconto” o “instradamento ticket di supporto”—e disegnala come albero. Poi riscrivilo come liste annidate (o JSON). Chiediti: quali sono i nodi, quali le foglie e quali trasformazioni servono?
Portare le idee di Lisp in qualsiasi stack
Anche senza Lisp, puoi adottare la mentalità: costruisci rappresentazioni tipo AST, usa la generazione di codice per il glue ripetitivo e standardizza pipeline data-first (parse → transform → evaluate). Molti team ottengono benefici semplicemente rendendo esplicite le rappresentazioni intermedie.
Se ti piace il principio REPL ma distribuisci feature in stack mainstream, puoi comunque riprendere lo spirito negli strumenti: cicli di iterazione ridotti, snapshot/rollback ed esplicita pianificazione prima dell’esecuzione. Koder.ai, per esempio, include una modalità di pianificazione oltre a snapshot e rollback per mantenere l’iterazione rapida più sicura—un’eco operativa del “cambia in fretta, ma resta in controllo” di Lisp.
L’influenza duratura di McCarthy è questa: la programmazione diventa più potente quando rendiamo il ragionamento stesso programmabile—e manteniamo il percorso dall’idea al sistema eseguibile il più diretto possibile.
Domande frequenti
Cosa significa “pensiero simbolico” in termini pratici?
Il pensiero simbolico rappresenta concetti e relazioni direttamente (per esempio: “customer”, “is-a”, “depends-on”, “if…then…”), e applica regole e trasformazioni a quelle rappresentazioni.
È più utile quando il problema è pieno di struttura, eccezioni e significato (motori di regole, pianificazione, compilatori, configurazioni, logiche di workflow), non solo aritmetica.
Qual è stato il contributo principale di John McCarthy oltre a coniare il termine “AI”?
McCarthy sostenne l'idea che il ragionamento potesse essere espresso come programmi—non solo calcoli.
Questa prospettiva ha influenzato:
- come rappresentiamo la conoscenza (simboli strutturati)
- come esploriamo soluzioni (ricerca, pianificazione, applicazione di regole)
- cosa ci aspettiamo dai linguaggi (espressività, estendibilità, iterazione rapida)
Perché le liste Lisp sono così importanti per rappresentare la conoscenza?
Le liste sono un modo minimale e flessibile per rappresentare “cose fatte di parti”. Poiché gli elementi di una lista possono essere a loro volta liste, si ottengono naturalmente strutture ad albero.
Questo rende semplice:
- estrarre parti (es.: un nodo “subject”)
- riscrivere sotto-alberi (trasformazioni)
- modellare regole ed espressioni in modo coerente
Cosa sono le S-expressions e perché contano?
Le S-expressions ti danno una forma uniforme per codice e dati: liste annidate.
Questa uniformità semplifica i sistemi perché:
- il parsing è più regolare
- le trasformazioni sono dirette (cammini nell'albero e riscrittura dei nodi)
- gli strumenti possono riutilizzare la stessa rappresentazione per formattazione, analisi e generazione
Qual è la differenza pratica tra macro e funzioni?
Una macro automatizza forme di codice ripetitive, non solo calcoli ripetuti.
Usa le macro quando vuoi:
- creare una piccola DSL (es.: per regole o test)
- imporre pattern (logging, timing, strumentazione)
- rimuovere boilerplate mantenendo i punti di chiamata leggibili
Se ti serve solo logica riutilizzabile, una funzione è quasi sempre la scelta migliore.
Perché la garbage collection è vista come una caratteristica di produttività nella storia di Lisp?
La garbage collection (GC) recupera automaticamente la memoria non più raggiungibile dal programma, riducendo intere categorie di bug (puntatori pendenti, doppi free).
È particolarmente utile quando il programma crea molte strutture a vita breve (liste/alberi/AST), perché permette di prototipare e rifattorizzare senza progettare subito uno schema di proprietà della memoria.
In che modo un REPL cambia il modo di sviluppare software?
Un REPL accorcia il ciclo “pensare → provare → osservare”. Puoi definire una funzione, eseguirla, modificarla e rieseguirla immediatamente.
Per ottenere benefici simili in ambienti non Lisp:
- usa shell interattive o notebook
- aggiungi test rapidi e modalità watch
- tieni esempi e fixture vicini al codice
Lettura correlata: /blog/the-repl-and-fast-feedback-loops
Come si sono diffuse le idee di Lisp nella programmazione mainstream?
Molti flussi di lavoro moderni riutilizzano le stesse idee di base:
- pipeline funzionali (
map/filter, composizione) - strumenti basati su AST (linters, formatter, codegen)
- DSL ed estendibilità (macro, template, plugin)
- esplorazione interattiva (notebook, console devtools)
Anche se non distribuisci Lisp, probabilmente usi quotidianamente abitudini derivate da Lisp.
Quali sono i veri compromessi e le idee sbagliate su Lisp?
I compromis principali sono:
- Sintassi insolita: le parentesi possono sembrare strane finché non ti affidi all'indentazione e alla navigazione strutturale.
- Ecosistema/assunzioni: alcuni dialetti Lisp hanno pool di librerie e talenti più ridotti rispetto agli stack mainstream.
- Variabilità delle prestazioni: “Lisp” non è un singolo profilo—qualche implementazione compila e ottimizza bene, altre meno; testa le esigenze di latenza/throuput anziché assumere.
L'approccio pratico è valutare l'idoneità in base al dominio e ai vincoli, non alla reputazione.
Come posso applicare le lezioni di McCarthy/Lisp in un codice moderno senza usare Lisp?
Prova questo esercizio di 10 minuti:
- Scegli una funzionalità reale (es.: “regole sconto”).
- Rappresentala come albero (nodi = concetti/regole; foglie = letterali/campi).
- Scrivila come dati annidati (JSON o strutture tipo lista).
- Elenca le trasformazioni necessarie (validare, riscrivere, valutare, spiegare).
Questo spesso mette in luce dove i pattern “codice-come-dati”, motori di regole o config simili a DSL semplificherebbero il sistema.