Perché il Lisp conta ancora dopo sei decenni
Il Lisp ha più di 65 anni e continua a influenzare i linguaggi moderni. Cosa lo rende così duraturo e cosa possono imparare oggi gli sviluppatori.

Ogni pochi anni qualcuno scrive un saggio dal titolo «Il Lisp è morto», e ogni pochi anni qualcun altro fa notare che metà delle funzionalità del suo linguaggio moderno preferito sono state prese in prestito dal Lisp. Garbage collection, funzioni di prima classe, closure, tipizzazione dinamica, omoiconicità, sviluppo guidato dal REPL, macro — tutto inventato o reso popolare nel Lisp prima che nascesse la maggior parte degli sviluppatori di oggi. Il Lisp è il tipo di progetto che richiede decenni per essere apprezzato appieno.
Eppure. Chiedi a uno sviluppatore in attività se ha mai usato il Lisp e la maggior parte risponderà di no. Chiedigli se avvierebbe un progetto di produzione in Lisp e la maggior parte ti guarderà storto. C'è un paradosso: le idee del Lisp hanno conquistato il mondo della programmazione, ma il linguaggio in sé resta di nicchia. Capire perché è più interessante dei soliti discorsi del tipo «il Lisp è bello, gli altri linguaggi no» che si trovano in gran parte della propaganda pro-Lisp.
Cosa il Lisp ha fatto davvero bene
Il Lisp fu creato da John McCarthy nel 1958. Per capire il contesto, il FORTRAN aveva appena un anno. Il COBOL non esisteva ancora. Il minicomputer PDP-1 sarebbe arrivato sul mercato solo due anni dopo. Il Lisp fu progettato su carta e implementato su un IBM 704 — una macchina che riempiva una stanza e aveva parole da 36 bit.
Nonostante questo, McCarthy fece scelte di design che restano attuali sei decenni dopo. Le più importanti:
Codice come dati (omoiconicità)
Nella maggior parte dei linguaggi, codice e dati sono cose profondamente diverse. Scrivi il codice in una sintassi, e questo manipola strutture dati. In Lisp, il codice è dati. Un programma Lisp è fatto di liste. L'espressione (+ 1 2) è contemporaneamente una chiamata di funzione che somma 1 e 2, e una lista di tre elementi: il simbolo +, il numero 1 e il numero 2. Puoi manipolare quella lista — riordinarla, aggiungere elementi, trasformarla — e poi eseguire il risultato.
;; This is a function call
(+ 1 2) ; => 3
;; This is a list containing the same elements
'(+ 1 2) ; => (+ 1 2)
;; You can build code as data and then evaluate it
(def my-expr '(+ 1 2))
(eval my-expr) ; => 3
;; Or transform it
(def doubled (list '* 2 my-expr))
;; doubled is now (* 2 (+ 1 2))
(eval doubled) ; => 6
Sembra una curiosità finché non ti rendi conto di cosa permette: programmi che scrivono programmi. Le macro Lisp non fanno una semplice sostituzione di testo come le macro del preprocessore C. Ricevono la struttura reale del programma come struttura dati, la trasformano usando tutta la potenza del linguaggio e restituiscono nuova struttura di programma. È generazione di codice a tempo di compilazione, con il compilatore stesso come API.
Il REPL come ambiente di sviluppo
Il Lisp ha introdotto il Read-Eval-Print Loop — l'idea che dovresti poter digitare un'espressione, vederla valutata immediatamente e osservarne il risultato. Oggi sembra ovvio, dato che ogni linguaggio ha un REPL, ma quello del Lisp va oltre la maggior parte degli altri.
In Common Lisp o Clojure non ti limiti a testare espressioni isolate nel REPL. Sviluppi interi sistemi in modo interattivo. Definisci una funzione, la testi, la ridefinisci, il tutto mentre il programma è in esecuzione. Puoi ispezionare e modificare lo stato in esecuzione. Puoi ricompilare una singola funzione senza riavviare l'applicazione. Il ciclo di sviluppo non è scrivi-compila-esegui-debugga — è una conversazione continua con un sistema vivo.
Gli sviluppatori Clojure lavorano spesso così: collegano l'editor a un REPL in esecuzione, scrivono codice nell'editor, valutano singole espressioni con la pressione di un tasto e vedono i risultati all'istante. Il ciclo di feedback si misura in millisecondi, non in minuti. Una volta lavorato in questo modo, il tradizionale ciclo modifica-compila-riavvia sembra comunicare per posta.
Sintassi minimale, massima flessibilità
La sintassi del Lisp — o la sua assenza — è la caratteristica più controversa. Non esistono forme speciali per le istruzioni if, i cicli for o le definizioni di classi. Tutto è una lista con l'operatore per primo: (if condizione espr-then espr-else), (defn nome [args] corpo), (for [x (range 10)] (* x x)). Le parentesi che i neofiti trovano sgradevoli sono il prezzo da pagare per il linguaggio sintatticamente più regolare mai progettato.
Questa regolarità porta un vantaggio pratico: gli strumenti. Quando ogni costrutto segue lo stesso schema (operatore operandi...), gli editor possono indentare, rifattorizzare e navigare il codice in modo strutturale in modo affidabile. «Seleziona l'espressione che racchiude» è inequivocabile nel Lisp — corrisponde sempre alle parentesi di apertura e chiusura. Nei linguaggi con sintassi varia, l'editing strutturale è un problema irrisolto. Nel Lisp, è stato risolto fin dagli anni Sessanta.
Perché il Lisp non ha vinto
Se il Lisp è così valido, perché non lo usano tutti? La risposta standard dei sostenitori del Lisp è «l'industria sbaglia», che non aiuta ed è per lo più falsa. Il Lisp non ha raggiunto l'adozione di massa per motivi concreti e non banali.
- Il divario dell'ecosistema. Per la maggior parte del lavoro pratico le librerie contano più delle funzionalità del linguaggio. Python non è popolare perché la sua sintassi è bella — lo è perché
pip installti dà accesso immediato a migliaia di pacchetti ben mantenuti per data science, sviluppo web, machine learning e tutto il resto. Gli ecosistemi Lisp (Common Lisp, Scheme, Clojure) sono solidi ma più piccoli. Passerai più tempo a scrivere cose da zero o ad adattare librerie meno mantenute. - La curva di apprendimento è concentrata all'inizio. La sintassi con parentesi del Lisp è banalmente semplice una volta interiorizzata, ma è un vero ostacolo per chi inizia. Soprattutto, scrivere Lisp idiomatico richiede di pensare in modo poco familiare per chi viene dalla programmazione procedurale o orientata agli oggetti. La ricompensa arriva più tardi, ma molti sviluppatori mollano prima di arrivarci.
- Frammentazione. «Lisp» non è un linguaggio solo — è una famiglia. Common Lisp, Scheme, Racket, Clojure, Emacs Lisp, Hy, Janet — ognuno con punti di forza, ecosistemi e comunità diversi. Questa frammentazione impedisce la concentrazione di sforzi che fa crescere gli ecosistemi.
- Il supporto aziendale conta. Java aveva Sun. C# aveva Microsoft. Go aveva Google. Python ha avuto Google e un'enorme comunità di data science. Il linguaggio più affermato della famiglia Lisp in produzione, Clojure, ha guadagnato terreno in parte perché Rich Hickey è un pensatore e comunicatore eccezionalmente chiaro — ma gli manca ancora la spinta istituzionale che guida l'adozione di massa.
Clojure: il Lisp che ha imparato dalla storia
Clojure merita un'attenzione particolare perché mostra come prendere i punti di forza del Lisp e renderli pratici per lo sviluppo software moderno. Rich Hickey ha progettato Clojure nel 2007 consapevole dei motivi per cui i Lisp precedenti non avevano raggiunto l'adozione di massa, e ha fatto scelte di compromesso deliberate.
- Gira sulla JVM. Invece di costruire un ecosistema separato da zero, Clojure gira sulla Java Virtual Machine e può usare direttamente qualsiasi libreria Java. Ti serve un driver per il database? Usi quello Java. Ti serve un client HTTP? Usi quello Java. Questa singola decisione ha dato a Clojure accesso a migliaia di librerie collaudate fin dal primo giorno.
- Immutabilità di default. Le strutture dati principali di Clojure — liste, vettori, mappe, insiemi — sono immutabili. Non modifichi una mappa, ne crei una nuova con le tue modifiche. Questo elimina intere categorie di bug di concorrenza e rende i programmi più facili da ragionarci sopra. Le strutture dati persistenti sotto il cofano condividono la struttura interna per rendere tutto efficiente.
- Primitive di concorrenza pratiche. Atom, ref, agent — Clojure offre più modelli di concorrenza, ciascuno adatto a pattern diversi. Non era eleganza teorica: era l'esperienza diretta di Hickey con i problemi dello stato mutabile condiviso nelle applicazioni Java in produzione.
- ClojureScript. Clojure compila in JavaScript, dando accesso agli ecosistemi del browser e di Node.js. Scrivi il backend in Clojure, il frontend in ClojureScript e condividi il codice tra i due. È la stessa promessa di TypeScript o Kotlin Multiplatform, ma arrivata prima.
Cosa hanno preso in prestito i linguaggi moderni
Anche se non scrivi mai una riga di Lisp, usi le sue idee ogni giorno. La sua influenza è così pervasiva che tracciarla diventa un esercizio per capire quanto in profondità arrivi.
map, filter e reduce di JavaScript discendono direttamente dalle funzioni di elaborazione delle liste del Lisp — il linguaggio prende il nome proprio da questo (LISt Processing). Le list comprehension di Python sono zucchero sintattico per le stesse operazioni. Il pattern matching di Rust risale attraverso ML fino alle espressioni cond del Lisp. Le closure di Swift, la sintassi lambda di Kotlin, le stream API di Java — sono tutti concetti Lisp con vesti diverse.
I sistemi di macro di Rust (macro_rules! e le macro procedurali), Elixir, Nim e Julia sono tutti direttamente ispirati alle macro Lisp — anche se nessuno raggiunge la stessa fluidità, perché nessuno di quei linguaggi ha una sintassi omoiconica. Quando il codice è dati, le macro sono banali. Quando il codice ha una sintassi complessa, le macro richiedono di analizzare e generare quella sintassi, e questo aggiunge attrito.
Lo sviluppo guidato dal REPL è diventato la norma nella data science (i notebook Jupyter sono essenzialmente REPL con persistenza), e strumenti come Figwheel e shadow-cljs hanno portato l'hot-reloading nello sviluppo frontend — un'idea che arriva direttamente dalla tradizione Lisp di sviluppare su un sistema in esecuzione.
Conviene imparare il Lisp?
La risposta onesta dipende da cosa vuoi ottenere.
Se vuoi diventare un programmatore migliore ampliando il modo in cui pensi al codice: sì, assolutamente. Imparare il Lisp — in particolare pensare in termini di trasformazione dei dati, strutture ricorsive e codice-come-dati — cambierà il modo in cui affronti i problemi in qualsiasi linguaggio. È come imparare la programmazione funzionale: anche se non userai mai Haskell in produzione, quei concetti ti fanno scrivere Python e JavaScript migliori.
Se vuoi costruire software di produzione con un Lisp: Clojure è la scelta pratica. Ha un ecosistema reale, una comunità professionale e aziende (Nubank, Walmart, CircleCI) che lo usano su larga scala. Common Lisp è valido ma di nicchia — passerai più tempo sull'infrastruttura e meno sul tuo problema reale. Scheme e Racket sono eccellenti per la didattica e la ricerca sui linguaggi, ma meno pratici per lo sviluppo generico.
Se sei curioso ma non pronto a impegnarti, leggi del codice Clojure. Non tutorial — codice di produzione vero. Guarda come gli sviluppatori Clojure compongono piccole funzioni, come usano le macro di threading (-> e ->>>) per costruire pipeline di dati, come modellano i domini con semplici mappe invece che con classi. Vedrai uno stile di programmazione più conciso, più componibile e più focalizzato sul flusso dei dati rispetto a quello che trovi nelle basi di codice OOP mainstream.
La longevità del Lisp non è nostalgia. È il risultato di aver azzeccato alcune cose fondamentali così bene che sei decenni di progettazione di linguaggi non le hanno migliorate. Le parentesi sono strane. L'ecosistema è più piccolo di quanto vorresti. Ma le idee sono senza tempo, e se ci passi del tempo, ti ritroverai a scrivere codice migliore in qualunque linguaggio usi ogni giorno.


