🧠 Lisp / Common Lisp — 1958

⬅️ Atlas · 🧟 Lenguajes que siguen vivos · 📚 Índice de clases

El segundo lenguaje de alto nivel de la historia, y el que se adelantó a todos. Recolección de basura, funciones de primera clase, tipado dinámico, REPL, código que se manipula a sí mismo: Lisp tenía todo eso cuando el resto del mundo programaba con tarjetas perforadas. Los demás lenguajes llevan sesenta años alcanzándolo.

🎯 Por qué está en este programa

Criterio de inclusión: Common Lisp se ejecuta hoy. SBCL publica una versión al mes, Quicklisp distribuye miles de bibliotecas, y hay implementaciones comerciales con soporte (LispWorks, Allegro CL) vendiéndose a industria. Su variante AutoLISP viene incluida en AutoCAD, uno de los programas de ingeniería más usados del planeta. Y Emacs Lisp configura el editor de una parte considerable de los programadores del mundo.

Entra porque muestra dos conceptos que el núcleo entero no tiene. El primero es la homoiconicidad: en Lisp el código y los datos tienen la misma forma —listas—, así que un programa puede construir y transformar programas con las mismas herramientas con que manipula una lista. De ahí salen las macros, que no son plantillas de texto como en C sino transformaciones reales del árbol sintáctico: en Lisp puedes añadir sintaxis al lenguaje. El segundo es el sistema de condiciones y reinicios, un manejo de errores estrictamente más potente que el try/catch que casi todos heredamos. Ver Lisp es entender qué le falta a los demás.

Año 1958 (McCarthy); Common Lisp estandarizado en ANSI X3.226 en 1994
Autoría John McCarthy (MIT); Common Lisp por un comité de la comunidad
Familia Lisp — la raíz de Scheme, Racket, Clojure, Emacs Lisp y AutoLISP
Paradigma Multiparadigma: funcional, imperativo y OO (CLOS, con despacho múltiple)
Tipado Dinámico y fuerte, con declaraciones de tipo opcionales para optimizar
Memoria Recolector de basura — Lisp fue donde se inventó
Ejecución Compilado a nativo en tiempo de ejecución; REPL siempre disponible
Estado 🟡 Nicho vivo — IA simbólica, CAD, sistemas con mucho DSL, investigación

📜 Historia

En 1958 John McCarthy trabajaba en el MIT sobre problemas de inteligencia artificial y necesitaba manipular expresiones simbólicas, no números. Su artículo de 1960, Recursive Functions of Symbolic Expressions and Their Computation by Machine, define un lenguaje matemático basado en el cálculo lambda y en una sola estructura de datos: la lista.

McCarthy había definido, como ejercicio teórico, una función eval que interpretaba expresiones del propio lenguaje. Su estudiante Steve Russell se dio cuenta de algo que McCarthy no había previsto: esa eval se podía implementar en código máquina, y entonces habría un intérprete de Lisp de verdad. Lo hizo, y así nació el primer Lisp ejecutable — junto con la idea del REPL y de la programación interactiva.

Lisp introdujo o popularizó, décadas antes que nadie:

Durante los 70 y 80 proliferaron dialectos: MacLisp, InterLisp, ZetaLisp, Scheme (1975, minimalista y académico). Se llegó a construir hardware específico —las máquinas Lisp de Symbolics y LMI— con el lenguaje como sistema operativo. Cuando la financiación de la IA se desplomó a finales de los 80 (el llamado "invierno de la IA"), aquella industria se hundió con ella.

Para frenar la fragmentación, un comité produjo en 1984 Common Lisp the Language y en 1994 el estándar ANSI Common Lisp: un lenguaje grande, deliberadamente pragmático, que unificó los dialectos e incorporó CLOS (Common Lisp Object System), probablemente el sistema de objetos más potente jamás estandarizado.

🏭 Dónde sobrevive hoy

🧠 Por qué no ha muerto

1. Las macros permiten construir el lenguaje que el problema necesita. Como el código es una lista, una macro recibe la estructura del código y devuelve otra estructura, antes de compilar. En un dominio complejo puedes crear notación propia y escribir la solución en ella. En otros lenguajes, eso exige un preprocesador, un generador de código o un DSL externo con su parser.

2. El sistema de condiciones es superior al try/catch. Cuando una función de Lisp señala un error, el manejador se ejecuta antes de desenrollar la pila, y puede elegir entre varios reinicios (restarts) que la función que falló dejó disponibles: reintentar, usar otro valor, saltar el elemento. En Java o Python, para cuando atrapas la excepción, el contexto ya se destruyó y la única opción es reintentar todo desde fuera. PL/I tuvo una idea parecida en 1964; casi nadie más la siguió.

3. Desarrollo interactivo real. Con SLIME/Sly sobre Emacs puedes recompilar una única función dentro de una imagen en ejecución, con el estado intacto. Es el mismo modelo de trabajo vivo de Smalltalk.

4. CLOS y el despacho múltiple. Un método de CLOS se selecciona según los tipos de todos sus argumentos, no solo del receptor. Eso resuelve limpiamente el problema del "doble despacho" que en Java o C# obliga al patrón Visitor. Además, el Protocolo de Metaobjetos (MOP) permite modificar cómo funciona la propia orientación a objetos.

5. Es rápido. SBCL compila a código nativo. La fama de "lenguaje lento e interpretado" viene de los años 70 y hoy es simplemente falsa.

🔄 Lo que se ha modernizado

El estándar ANSI está congelado desde 1994 —eso es una decisión, no un abandono—, pero todo lo que rodea al lenguaje se ha renovado:

⚙️ Cómo se ejecuta hoy

sudo apt-get install -y sbcl

sbcl --script total-venta.lisp < entrada.txt

# El REPL, que es donde de verdad se trabaja:
sbcl
* (+ 1 2)
3

Implementaciones: SBCL (Steel Bank Common Lisp, libre, la más usada y la más rápida), CCL (Clozure), ECL (empotrable en C), ABCL (sobre la JVM), y las comerciales LispWorks y Allegro CL (Franz), con IDE, entrega de aplicaciones y soporte.

Ecosistema: Quicklisp es el gestor de bibliotecas de facto y ASDF el sistema de construcción. Para editar, Emacs + SLIME/Sly sigue siendo la experiencia de referencia, aunque hay extensiones para VS Code (Alive) y un plugin para IntelliJ.

🧪 El programa de la clase 041 en Common Lisp

;;; total-venta.lisp — clase 041
(setf *read-default-float-format* 'double-float)

(let* ((precio    (read))
       (cantidad  (read))
       (descuento (read))
       (total     (* precio cantidad (- 1 descuento))))
  (format t "Total: ~,2F~%" total))

Recorrido, línea a línea.

Y ahora el motivo real de estudiarlo. Supón que quieres una notación propia para las reglas de precios de tu negocio. En Lisp la creas:

(defmacro con-descuento ((var porcentaje) &body cuerpo)
  "Ejecuta CUERPO con VAR ligada al factor multiplicador del descuento."
  `(let ((,var (- 1 (/ ,porcentaje 100))))
     ,@cuerpo))

(con-descuento (factor 10)
  (format t "Total: ~,2F~%" (* 15000 2 factor)))
;; => Total: 27000.00

con-descuento no es una función: es una macro que se ejecuta en tiempo de compilación y reescribe el código antes de compilarlo. El acento grave abre una plantilla, la coma inserta un valor y ,@ inserta una lista de expresiones. El resultado es una construcción sintáctica nueva, indistinguible de las que trae el lenguaje. En Python o Java, para conseguir esto harías falta un generador de código externo; en Lisp es una función más que devuelve listas, porque el código es una lista.

🔍 Qué reconocer si vienes de otro lenguaje

Si conoces… En Common Lisp es…
a + b * c (+ a (* b c)) — prefijo, sin precedencia que memorizar
x = 5 (setf x 5) — y setf funciona sobre cualquier "lugar"
Variable local (let ((x 1) (y 2)) ...)
def f(x): ... (defun f (x) ...)
Lambda (lambda (x) ...)
if / elif / else (if c a b) o (cond (c1 ...) (c2 ...) (t ...))
switch (case x (1 ...) (2 ...) (otherwise ...))
Clase con métodos defclass + defgeneric + defmethod — los métodos viven fuera de la clase
try / catch / finally handler-case, handler-bind + restart-case, unwind-protect
Anotaciones / decoradores defmacro — pero de verdad
null nil, que además es la lista vacía y el falso lógico

⚠️ Errores comunes al leerlo

📚 Fuentes y bibliografía


⏮️ Volver al Atlas · 🧟 Los lenguajes que siguen vivos · 🔗 Relacionadas: AutoLISP · Smalltalk · PL/I