Qué Hizo Diferente a Lisp

Diciembre de 2001 (rev. Mayo de 2002)

(Este artículo surgió en respuesta a algunas preguntas en la lista de correo LL1. Ahora está incorporado en Revenge of the Nerds.)

Cuando McCarthy diseñó Lisp a finales de los años 50, supuso una ruptura radical con los lenguajes existentes, el más importante de los cuales era Fortran.

Lisp incorporó nueve ideas nuevas:


1. Condicionales. Un condicional es una construcción if-then-else. Hoy en día las damos por sentadas. Fueron inventadas por McCarthy en el curso del desarrollo de Lisp. (Fortran en ese momento solo tenía un goto condicional, basado estrechamente en la instrucción de salto del hardware subyacente.) McCarthy, que estaba en el comité de Algol, introdujo los condicionales en Algol, de donde se extendieron a la mayoría de los otros lenguajes.

2. Un tipo de función. En Lisp, las funciones son objetos de primera clase: son un tipo de dato como los enteros, las cadenas, etc., y tienen una representación literal, pueden almacenarse en variables, pasarse como argumentos, etc.

3. Recursión. La recursión existía como concepto matemático antes de Lisp, por supuesto, pero Lisp fue el primer lenguaje de programación en soportarla. (Es discutiblemente implícita en hacer de las funciones objetos de primera clase.)

4. Un nuevo concepto de variables. En Lisp, todas las variables son efectivamente punteros. Los valores son los que tienen tipos, no las variables, y asignar o enlazar variables significa copiar punteros, no lo que apuntan.

5. Recolección de basura.

6. Programas compuestos por expresiones. Los programas Lisp son árboles de expresiones, cada una de las cuales devuelve un valor. (En algunos Lisp, las expresiones pueden devolver múltiples valores.) Esto contrasta con Fortran y la mayoría de los lenguajes subsiguientes, que distinguen entre expresiones y sentencias.

Era natural tener esta distinción en Fortran porque (no sorprendentemente en un lenguaje donde el formato de entrada eran tarjetas perforadas) el lenguaje estaba orientado a líneas. No se podían anidar sentencias. Y así, aunque se necesitaban expresiones para que las matemáticas funcionaran, no tenía sentido hacer que cualquier otra cosa devolviera un valor, porque no podía haber nada esperando por él.

Esta limitación desapareció con la llegada de los lenguajes estructurados por bloques, pero para entonces ya era demasiado tarde. La distinción entre expresiones y sentencias estaba arraigada. Se extendió de Fortran a Algol y de ahí a ambos descendientes.

Cuando un lenguaje está hecho enteramente de expresiones, puedes componer expresiones como quieras. Puedes decir, ya sea (usando la sintaxis de Arc)

(if foo (= x 1) (= x 2))

o

(= x (if foo 1 2))

7. Un tipo de símbolo. Los símbolos difieren de las cadenas en que puedes probar la igualdad comparando un puntero.

8. Una notación para el código usando árboles de símbolos.

9. Todo el lenguaje siempre disponible. No hay una distinción real entre tiempo de lectura, tiempo de compilación y tiempo de ejecución. Puedes compilar o ejecutar código mientras lees, leer o ejecutar código mientras compilas, y leer o compilar código en tiempo de ejecución.

Ejecutar código en tiempo de lectura permite a los usuarios reprogramar la sintaxis de Lisp; ejecutar código en tiempo de compilación es la base de las macros; compilar en tiempo de ejecución es la base del uso de Lisp como lenguaje de extensión en programas como Emacs; y leer en tiempo de ejecución permite a los programas comunicarse utilizando s-expresiones, una idea recientemente reinventada como XML.


Cuando Lisp se inventó por primera vez, todas estas ideas estaban muy alejadas de la práctica de programación ordinaria, que estaba dictada en gran medida por el hardware disponible a finales de los años 50.

Con el tiempo, el lenguaje por defecto, encarnado en una sucesión de lenguajes populares, ha evolucionado gradualmente hacia Lisp. Los puntos 1-5 están ahora muy extendidos. El punto 6 está empezando a aparecer en la corriente principal. Python tiene una forma del punto 7, aunque no parece haber ninguna sintaxis para ello. El punto 8, que (junto con el 9) es lo que hace posibles las macros de Lisp, sigue siendo hasta ahora único en Lisp, quizás porque (a) requiere esos paréntesis, o algo igual de malo, y (b) si añades ese último incremento de potencia, ya no puedes afirmar haber inventado un nuevo lenguaje, sino solo haber diseñado un nuevo dialecto de Lisp ;-)

Aunque útil para los programadores de hoy en día, es extraño describir Lisp en términos de su variación respecto a los expedientes aleatorios que adoptaron otros lenguajes. Esa no fue, probablemente, la forma en que McCarthy lo pensó. Lisp no fue diseñado para corregir los errores de Fortran; surgió más bien como un subproducto de un intento de axiomatizar la computación.