SER POPULAR
OriginalMayo 2001
(Este artículo fue escrito como una especie de plan de negocios para un nuevo lenguaje. Por lo tanto, le falta (porque se da por sentado) la característica más importante de un buen lenguaje de programación: abstracciones muy poderosas.)
Un amigo mío le dijo una vez a un eminente experto en sistemas operativos que quería diseñar un lenguaje de programación realmente bueno. El experto le dijo que sería una pérdida de tiempo, que los lenguajes de programación no se vuelven populares o impopulares en función de sus méritos, y que por lo tanto, sin importar cuán bueno fuera su lenguaje, nadie lo usaría. Al menos, eso es lo que le había sucedido al lenguaje que él había diseñado.
¿Qué es lo que hace que un lenguaje sea popular? ¿Los lenguajes populares merecen su popularidad? ¿Vale la pena intentar definir un buen lenguaje de programación? ¿Cómo lo harías?
Creo que las respuestas a estas preguntas se pueden encontrar mirando a los hackers y aprendiendo lo que quieren. Los lenguajes de programación son para los hackers, y un lenguaje de programación es bueno como lenguaje de programación (en lugar de, digamos, un ejercicio en semántica denotacional o diseño de compiladores) si y solo si a los hackers les gusta.
1 La Mecánica de la Popularidad
Es cierto, sin duda, que la mayoría de las personas no eligen los lenguajes de programación simplemente en función de sus méritos. La mayoría de los programadores se les dice qué lenguaje usar por alguien más. Y sin embargo creo que el efecto de tales factores externos en la popularidad de los lenguajes de programación no es tan grande como a veces se cree. Creo que un problema mayor es que la idea de un hacker de un buen lenguaje de programación no es la misma que la de la mayoría de los diseñadores de lenguajes.
Entre los dos, la opinión del hacker es la que importa. Los lenguajes de programación no son teoremas. Son herramientas, diseñadas para las personas, y tienen que diseñarse para adaptarse a las fortalezas y debilidades humanas tanto como los zapatos tienen que diseñarse para los pies humanos. Si un zapato aprieta cuando te lo pones, es un mal zapato, por más elegante que pueda ser como pieza de escultura.
Puede ser que la mayoría de los programadores no puedan distinguir un buen lenguaje de uno malo. Pero eso no es diferente con ninguna otra herramienta. No significa que sea una pérdida de tiempo intentar diseñar un buen lenguaje. Los hackers expertos pueden reconocer un buen lenguaje cuando lo ven, y lo usarán. Los hackers expertos son una minoría diminuta, admitidamente, pero esa diminuta minoría escribe todo el buen software, y su influencia es tal que el resto de los programadores tenderán a usar cualquier lenguaje que ellos usen. A menudo, de hecho, no es meramente influencia sino mandato: a menudo los hackers expertos son los mismos que, como sus jefes o asesores de la facultad, les dicen a los otros programadores qué lenguaje usar.
La opinión de los hackers expertos no es la única fuerza que determina la relativa popularidad de los lenguajes de programación — el software heredado (Cobol) y la hype (Ada, Java) también juegan un papel — pero creo que es la fuerza más poderosa a largo plazo. Dada una masa crítica inicial y suficiente tiempo, un lenguaje de programación probablemente se vuelve aproximadamente tan popular como se merece. Y la popularidad separa aún más a los buenos lenguajes de los malos, porque la retroalimentación de usuarios reales siempre conduce a mejoras. Mira cuánto ha cambiado cualquier lenguaje popular durante su vida. Perl y Fortran son casos extremos, pero incluso Lisp ha cambiado mucho. Lisp 1.5 no tenía macros, por ejemplo; estos evolucionaron más tarde, después de que los hackers del MIT hubieran pasado un par de años usando Lisp para escribir programas reales. [1]
Así que ya sea que un lenguaje tenga que ser bueno para ser popular o no, creo que un lenguaje tiene que ser popular para ser bueno. Y tiene que seguir siendo popular para seguir siendo bueno. El estado del arte en lenguajes de programación no se detiene. Y sin embargo los Lisps que tenemos hoy en día son todavía más o menos lo que tenían en el MIT a mediados de la década de 1980, porque esa es la última vez que Lisp tuvo una base de usuarios lo suficientemente grande y exigente.
Por supuesto, los hackers tienen que saber de un lenguaje antes de poder usarlo. ¿Cómo se van a enterar? De otros hackers. Pero tiene que haber algún grupo inicial de hackers usando el lenguaje para que otros puedan incluso oír hablar de él. Me pregunto cuán grande tiene que ser este grupo; ¿cuántos usuarios hacen una masa crítica? Así, a ojo, diría que veinte. Si un lenguaje tuviera veinte usuarios separados, es decir, veinte usuarios que decidieron por su cuenta usarlo, lo consideraría real.
Llegar ahí no puede ser fácil. No me sorprendería si fuera más difícil pasar de cero a veinte que de veinte a mil. La mejor manera de conseguir esos veinte usuarios iniciales probablemente sea usar un caballo de Troya: dar a la gente una aplicación que quieren, que casualmente esté escrita en el nuevo lenguaje.
2 Factores Externos
Empecemos por reconocer un factor externo que sí afecta la popularidad de un lenguaje de programación. Para volverse popular, un lenguaje de programación tiene que ser el lenguaje de scripting de un sistema popular. Fortran y Cobol fueron los lenguajes de scripting de los primeros mainframes de IBM. C fue el lenguaje de scripting de Unix, y más tarde, también lo fue Perl. Tcl es el lenguaje de scripting de Tk. Java y Javascript están destinados a ser los lenguajes de scripting de los navegadores web.
Lisp no es un lenguaje masivamente popular porque no es el lenguaje de scripting de un sistema masivamente popular. La popularidad que conserva se remonta a las décadas de 1960 y 1970, cuando era el lenguaje de scripting del MIT. Muchos de los grandes programadores de la época estuvieron asociados con el MIT en algún momento. Y a principios de la década de 1970, antes de C, el dialecto de Lisp del MIT, llamado MacLisp, era uno de los únicos lenguajes de programación que un hacker serio querría usar.
Hoy en día, Lisp es el lenguaje de scripting de dos sistemas moderadamente populares, Emacs y Autocad, y por esa razón sospecho que la mayor parte de la programación en Lisp que se hace hoy se hace en Emacs Lisp o AutoLisp.
Los lenguajes de programación no existen de forma aislada. Hackear es un verbo transitivo — los hackers suelen estar hackeando algo — y en la práctica, los lenguajes se juzgan en relación con lo que se usan para hackear. Así que si quieres diseñar un lenguaje popular, o bien tienes que proporcionar algo más que un lenguaje, o tienes que diseñar tu lenguaje para reemplazar el lenguaje de scripting de algún sistema existente.
El Lisp común es poco popular en parte porque es un huérfano. Originalmente vino con un sistema para hackear: la Máquina Lisp. Pero las Máquinas Lisp (junto con las computadoras paralelas) fueron aplastadas por el creciente poder de los procesadores de propósito general en la década de 1980. El Lisp común podría haber seguido siendo popular si hubiera sido un buen lenguaje de secuencias de comandos para Unix. Es, por desgracia, uno terriblemente malo.
Una forma de describir esta situación es decir que un lenguaje no se juzga por sus propios méritos. Otra opinión es que un lenguaje de programación realmente no es un lenguaje de programación a menos que también sea el lenguaje de secuencias de comandos de algo. Esto solo parece injusto si es una sorpresa. Creo que no es más injusto que esperar que un lenguaje de programación tenga, digamos, una implementación. Es solo parte de lo que es un lenguaje de programación.
Un lenguaje de programación necesita una buena implementación, por supuesto, y esto debe ser gratuito. Las empresas pagarán por el software, pero los hackers individuales no lo harán, y son los hackers a los que necesitas atraer.
Un lenguaje también necesita tener un libro sobre él. El libro debe ser delgado, bien escrito y lleno de buenos ejemplos. K&R es el ideal aquí. En este momento, casi diría que un lenguaje tiene que tener un libro publicado por O'Reilly. Eso se está convirtiendo en la prueba de importar para los hackers.
También debe haber documentación en línea. De hecho, el libro puede comenzar como documentación en línea. Pero no creo que los libros físicos estén pasados de moda todavía. Su formato es conveniente, y la censura de facto impuesta por los editores es un filtro útil, aunque imperfecto. Las librerías son uno de los lugares más importantes para aprender sobre nuevos lenguajes.
3 Brevedad
Dado que puede suministrar las tres cosas que necesita cualquier lenguaje: una implementación gratuita, un libro y algo que hackear, ¿cómo crea un lenguaje que a los hackers les guste?
Una cosa que les gusta a los hackers es la brevedad. Los hackers son perezosos, de la misma manera que los matemáticos y los arquitectos modernistas son perezosos: odian todo lo que es superfluo. No estaría lejos de la verdad decir que un hacker a punto de escribir un programa decide qué lenguaje usar, al menos subconscientemente, en función del número total de caracteres que tendrá que escribir. Si esto no es precisamente cómo piensan los hackers, un diseñador de lenguaje haría bien en actuar como si lo fuera.
Es un error tratar de cuidar al usuario con expresiones largas y tediosas que se supone que se parecen al inglés. Cobol es notorio por este defecto. Un hacker consideraría que se le pida escribir
agregar x a y dando z
en lugar de
z = x+y
como algo entre un insulto a su inteligencia y un pecado contra Dios.
A veces se ha dicho que Lisp debería usar primero y resto en lugar de coche y cdr, porque haría que los programas fueran más fáciles de leer. Tal vez durante las primeras dos horas. Pero un hacker puede aprender rápidamente que coche significa el primer elemento de una lista y cdr significa el resto. Usar primero y resto significa un 50% más de escritura. Y también tienen diferentes longitudes, lo que significa que los argumentos no se alinearán cuando se llamen, como a menudo lo hacen coche y cdr, en líneas sucesivas. He descubierto que importa mucho cómo se alinea el código en la página. Apenas puedo leer el código de Lisp cuando se establece en una fuente de ancho variable, y los amigos dicen que esto también es cierto para otros idiomas.
La brevedad es un lugar donde los lenguajes fuertemente tipados pierden. Todas las demás cosas siendo iguales, nadie quiere comenzar un programa con una serie de declaraciones. Todo lo que se pueda hacer implícito, debe serlo.
Los tokens individuales también deben ser cortos. Perl y Common Lisp ocupan polos opuestos en esta cuestión. Los programas de Perl pueden ser casi crípticos y densos, mientras que los nombres de los operadores integrados de Common Lisp son cómicamente largos. Los diseñadores de Common Lisp probablemente esperaban que los usuarios tuvieran editores de texto que les escribieran estos nombres largos. Pero el costo de un nombre largo no es solo el costo de escribirlo. También está el costo de leerlo y el costo del espacio que ocupa en la pantalla.
4 Hackabilidad
Hay una cosa más importante que la brevedad para un hacker: poder hacer lo que quieras. En la historia de los lenguajes de programación, se ha dedicado una sorprendente cantidad de esfuerzo a evitar que los programadores hagan cosas que se consideran impropias. Este es un plan peligrosamente presumido. ¿Cómo puede el diseñador del lenguaje saber lo que el programador va a necesitar hacer? Creo que los diseñadores de lenguaje harían mejor en considerar a su usuario objetivo como un genio que necesitará hacer cosas que nunca anticiparon, en lugar de un torpe que necesita ser protegido de sí mismo. El torpe se disparará en el pie de todos modos. Puede salvarlo de hacer referencia a variables en otro paquete, pero no puede salvarlo de escribir un programa mal diseñado para resolver el problema equivocado y tardar una eternidad en hacerlo.
Los buenos programadores a menudo quieren hacer cosas peligrosas y poco éticas. Por poco ético me refiero a cosas que van detrás de cualquier fachada semántica que el lenguaje esté tratando de presentar: obtener la representación interna de alguna abstracción de alto nivel, por ejemplo. A los hackers les gusta hackear, y hackear significa entrar en las cosas y adivinar al diseñador original.
Deja que te adivinen. Cuando creas cualquier herramienta, la gente la usa de maneras que no pretendías, y esto es especialmente cierto de una herramienta altamente articulada como un lenguaje de programación. Muchos hackers querrán modificar tu modelo semántico de una manera que nunca imaginaste. Digo, déjalos; dale al programador acceso a la mayor cantidad posible de cosas internas sin poner en peligro los sistemas en tiempo de ejecución como el recolector de basura.
En Common Lisp a menudo he querido iterar a través de los campos de una estructura: para sacar referencias a un objeto eliminado, por ejemplo, o encontrar campos que no se han inicializado. Sé que las estructuras son solo vectores por debajo. Y sin embargo, no puedo escribir una función de propósito general que pueda llamar a cualquier estructura. Solo puedo acceder a los campos por nombre, porque eso es lo que se supone que significa una estructura.
Un hacker puede que solo quiera subvertir el modelo previsto de las cosas una o dos veces en un programa grande. Pero qué diferencia hace poder hacerlo. Y puede ser más que una cuestión de simplemente resolver un problema. Aquí también hay un tipo de placer. Los hackers comparten el placer secreto del cirujano al hurgar en las vísceras repugnantes, el placer secreto del adolescente al reventar granos. [2] Para los niños, al menos, ciertos tipos de horrores son fascinantes. Maxim magazine publica un volumen anual de fotografías, que contiene una mezcla de pin-ups y accidentes macabros. Conocen a su público.
[1] Lisp Machines and parallel computers were steamrollered by the increasing power of general purpose processors in the 1980s.
[2] There is a kind of pleasure here too. Hackers share the surgeon's secret pleasure in poking about in gross innards, the teenager's secret pleasure in popping zits.
Históricamente, Lisp ha sido bueno para permitir que los hackers tengan su camino. La corrección política de Common Lisp es una aberración. Los primeros Lisps te permitían tener acceso a todo. Una gran parte de ese espíritu se conserva, afortunadamente, en los macros. Qué cosa tan maravillosa poder hacer transformaciones arbitrarias en el código fuente.
Los macros clásicos son una herramienta real de hacker: simples, poderosos y peligrosos. Es tan fácil entender lo que hacen: llamas a una función con los argumentos del macro y lo que devuelva se inserta en el lugar de la llamada al macro. Los macros higiénicos encarnan el principio opuesto. Intentan protegerte de entender lo que están haciendo. Nunca he oído explicar los macros higiénicos en una sola frase. Y son un ejemplo clásico de los peligros de decidir lo que los programadores pueden querer. Los macros higiénicos están destinados a protegerme de la captura de variables, entre otras cosas, pero la captura de variables es exactamente lo que quiero en algunos macros.
Un lenguaje realmente bueno debe ser limpio y sucio: diseñado con limpieza, con un pequeño núcleo de operadores bien entendidos y altamente ortogonales, pero sucio en el sentido de que permite a los hackers hacer lo que quieran con él. C es así. También lo eran los primeros Lisps. Un lenguaje de verdadero hacker siempre tendrá un carácter ligeramente desaliñado.
Un buen lenguaje de programación debe tener características que hagan que el tipo de personas que usan la frase "ingeniería de software" sacudan la cabeza con desaprobación. En el otro extremo del continuo están lenguajes como Ada y Pascal, modelos de propiedad que son buenos para enseñar y poco más.
5 Programas desechables
Para ser atractivo para los hackers, un lenguaje debe ser bueno para escribir los tipos de programas que quieren escribir. Y eso significa, quizás sorprendentemente, que debe ser bueno para escribir programas desechables.
Un programa desechable es un programa que escribes rápidamente para alguna tarea limitada: un programa para automatizar alguna tarea de administración del sistema, o generar datos de prueba para una simulación, o convertir datos de un formato a otro. Lo sorprendente de los programas desechables es que, como los edificios "temporales" construidos en tantas universidades estadounidenses durante la Segunda Guerra Mundial, a menudo no se desechan. Muchos evolucionan hasta convertirse en programas reales, con características reales y usuarios reales.
Tengo la impresión de que los mejores programas grandes comienzan así, en lugar de ser diseñados grandes desde el principio, como la presa Hoover. Es aterrador construir algo grande desde cero. Cuando la gente se enfrenta a un proyecto demasiado grande, se sienten abrumados. El proyecto se atasca o el resultado es estéril y rígido: un centro comercial en lugar de un centro real, Brasilia en lugar de Roma, Ada en lugar de C.
Otra forma de obtener un programa grande es comenzar con un programa desechable y seguir mejorándolo. Este enfoque es menos abrumador, y el diseño del programa se beneficia de la evolución. Creo que, si se mirara, se descubriría que esta es la forma en que se han desarrollado la mayoría de los programas grandes. Y los que evolucionaron de esta manera probablemente sigan escritos en el lenguaje en el que se escribieron por primera vez, porque es raro que un programa se porte, excepto por razones políticas. Y, paradójicamente, si quieres hacer un lenguaje que se use para sistemas grandes, tienes que hacerlo bueno para escribir programas desechables, porque de ahí es de donde provienen los sistemas grandes.
Perl es un ejemplo sorprendente de esta idea. No solo se diseñó para escribir programas desechables, sino que era prácticamente un programa desechable en sí mismo. Perl nació como una colección de utilidades para generar informes, y solo evolucionó a un lenguaje de programación a medida que los programas desechables que la gente escribía en él se volvían más grandes. No fue hasta Perl 5 (si es que entonces) que el lenguaje era adecuado para escribir programas serios, y sin embargo ya era enormemente popular.
¿Qué hace que un lenguaje sea bueno para los programas desechables? Para empezar, debe estar fácilmente disponible. Un programa desechable es algo que esperas escribir en una hora. Así que el lenguaje probablemente ya debe estar instalado en el ordenador que estás usando. No puede ser algo que tengas que instalar antes de usarlo. Tiene que estar ahí. C estaba ahí porque venía con el sistema operativo. Perl estaba ahí porque originalmente era una herramienta para administradores de sistemas, y el tuyo ya lo había instalado.
Estar disponible significa más que estar instalado, sin embargo. Un lenguaje interactivo, con una interfaz de línea de comandos, está más disponible que uno que tienes que compilar y ejecutar por separado. Un lenguaje de programación popular debe ser interactivo y arrancar rápido.
Otra cosa que quieres en un programa desechable es la brevedad. La brevedad siempre es atractiva para los hackers, y nunca más que en un programa que esperan producir en una hora.
6 Bibliotecas
Por supuesto, lo máximo en brevedad es que el programa ya esté escrito y solo tengas que llamarlo. Y esto nos lleva a lo que creo que será una característica cada vez más importante de los lenguajes de programación: las funciones de biblioteca. Perl gana porque tiene grandes bibliotecas para manipular cadenas. Esta clase de funciones de biblioteca son especialmente importantes para los programas desechables, que a menudo se escriben originalmente para convertir o extraer datos. Muchos programas de Perl probablemente comiencen como solo un par de llamadas a la biblioteca pegadas.
Creo que muchos de los avances que ocurran en los lenguajes de programación en los próximos cincuenta años tendrán que ver con las funciones de biblioteca. Creo que los futuros lenguajes de programación tendrán bibliotecas tan cuidadosamente diseñadas como el lenguaje central. El diseño de lenguajes de programación no se tratará de si hacer que tu lenguaje sea fuertemente o débilmente tipado, u orientado a objetos, o funcional, o lo que sea, sino de cómo diseñar grandes bibliotecas. El tipo de diseñadores de lenguajes que les gusta pensar en cómo diseñar sistemas de tipos pueden estremecerse ante esto. ¡Es casi como escribir aplicaciones! Qué pena. Los lenguajes son para los programadores, y las bibliotecas son lo que necesitan los programadores.
Diseñar buenas bibliotecas es difícil. No se trata simplemente de escribir mucho código. Una vez que las bibliotecas se vuelven demasiado grandes, a veces puede llevar más tiempo encontrar la función que necesitas que escribir el código tú mismo. Las bibliotecas deben diseñarse utilizando un pequeño conjunto de operadores ortogonales, al igual que el lenguaje central. Debería ser posible que el programador adivine qué llamada a la biblioteca hará lo que necesita.
Las bibliotecas son un lugar donde el Common Lisp se queda corto. Hay solo bibliotecas rudimentarias para manipular cadenas de texto, y casi ninguna para comunicarse con el sistema operativo. Por razones históricas, el Common Lisp intenta pretender que el sistema operativo no existe. Y debido a que no se puede comunicar con el sistema operativo, es poco probable que puedas escribir un programa serio usando solo los operadores integrados en el Common Lisp. También tienes que usar algunos trucos específicos de la implementación, y en la práctica estos tienden a no darte todo lo que quieres. Los hackers pensarían mucho mejor del Lisp si el Common Lisp tuviera bibliotecas de cadenas de texto poderosas y un buen soporte para el sistema operativo.
7 Sintaxis
¿Podría un lenguaje con la sintaxis del Lisp, o más precisamente, la falta de sintaxis, llegar a ser popular? No sé la respuesta a esta pregunta. Creo que la sintaxis no es la razón principal por la que el Lisp no es popular actualmente. El Common Lisp tiene problemas peores que una sintaxis poco familiar. Conozco varios programadores que se sienten cómodos con la sintaxis prefija y, sin embargo, usan Perl de forma predeterminada, porque tiene bibliotecas de cadenas de texto poderosas y puede comunicarse con el sistema operativo.
Hay dos posibles problemas con la notación prefija: que es poco familiar para los programadores y que no es lo suficientemente densa. La sabiduría convencional en el mundo del Lisp es que el primer problema es el real. No estoy tan seguro. Sí, la notación prefija hace que los programadores comunes se alarmen. Pero no creo que la opinión de los programadores comunes importe. Los lenguajes se vuelven populares o impopulares en función de lo que piensan de ellos los hackers expertos, y creo que los hackers expertos podrían lidiar con la notación prefija. La sintaxis de Perl puede ser bastante incomprensible, pero eso no ha impedido la popularidad de Perl. De hecho, puede haber ayudado a fomentar un culto a Perl.
Un problema más grave es la difusión de la notación prefija. Para los hackers expertos, ese realmente es un problema. Nadie quiere escribir (aref a x y) cuando podrían escribir a[x,y].
En este caso en particular, hay una forma de sortear el problema. Si tratamos las estructuras de datos como si fueran funciones en los índices, podríamos escribir (a x y) en su lugar, que es incluso más corto que la forma de Perl. Trucos similares pueden acortar otros tipos de expresiones.
Podemos eliminar (o hacer opcionales) muchos paréntesis haciendo que la indentación sea significativa. Así es como los programadores leen el código de todos modos: cuando la indentación dice una cosa y los delimitadores dicen otra, nos guiamos por la indentación. Tratar la indentación como significativa eliminaría esta fuente común de errores y también haría que los programas fueran más cortos.
A veces la sintaxis infija es más fácil de leer. Esto es especialmente cierto para las expresiones matemáticas. He usado Lisp durante toda mi vida de programación y aún no encuentro naturales las expresiones matemáticas prefijas. Y sin embargo, es conveniente, especialmente cuando estás generando código, tener operadores que tomen cualquier número de argumentos. Así que si tenemos sintaxis infija, probablemente debería implementarse como una especie de macro de lectura.
No creo que debamos oponernos religiosamente a introducir sintaxis en Lisp, siempre que se traduzca de una manera bien entendida en s-expresiones subyacentes. Ya hay una buena cantidad de sintaxis en Lisp. No es necesariamente malo introducir más, siempre que nadie se vea obligado a usarla. En Common Lisp, algunos delimitadores están reservados para el lenguaje, lo que sugiere que al menos algunos de los diseñadores tenían la intención de tener más sintaxis en el futuro.
Una de las piezas de sintaxis más escandalosamente poco lisp en Common Lisp se produce en las cadenas de formato; format es un lenguaje por derecho propio, y ese lenguaje no es Lisp. Si hubiera un plan para introducir más sintaxis en Lisp, los especificadores de formato podrían incluirse en él. Sería una buena cosa si las macros pudieran generar especificadores de formato de la misma manera que generan cualquier otro tipo de código.
Un destacado hacker de Lisp me dijo que su copia de CLTL se abre a la sección format. La mía también. Esto probablemente indique que hay margen de mejora. También puede significar que los programas hacen mucha E/S.
8 Eficiencia
Un buen lenguaje, como todo el mundo sabe, debe generar código rápido. Pero en la práctica, no creo que el código rápido provenga principalmente de las cosas que se hacen en el diseño del lenguaje. Como señaló Knuth hace mucho tiempo, la velocidad solo importa en ciertos cuellos de botella críticos. Y como han observado muchos programadores desde entonces, a menudo nos equivocamos sobre dónde están estos cuellos de botella.
Entonces, en la práctica, la forma de obtener código rápido es tener un perfilador muy bueno, en lugar de, por ejemplo, hacer que el lenguaje sea fuertemente tipado. No necesitas conocer el tipo de cada argumento en cada llamada del programa. Necesitas poder declarar los tipos de argumentos en los cuellos de botella. Y aún más, necesitas poder averiguar dónde están los cuellos de botella.
Una queja que la gente ha tenido con Lisp es que es difícil saber qué es costoso. Esto podría ser cierto. También podría ser inevitable, si quieres tener un lenguaje muy abstracto. Y en cualquier caso, creo que un buen perfilado iría un largo camino hacia solucionar el problema: pronto aprenderías qué era costoso.
Parte del problema aquí es social. A los diseñadores de lenguajes les gusta escribir compiladores rápidos. Así es como miden su habilidad. Ven al perfilador como un complemento, como mucho. Pero en la práctica, un buen perfilador puede hacer más para mejorar la velocidad de los programas reales escritos en el lenguaje que un compilador que genera código rápido. Aquí, una vez más, los diseñadores de lenguajes están un poco desconectados de sus usuarios. Hacen un trabajo realmente bueno resolviendo ligeramente el problema equivocado.
Podría ser una buena idea tener un perfilador activo, para empujar los datos de rendimiento al programador en lugar de esperar a que él los solicite. Por ejemplo, el editor podría mostrar los cuellos de botella en rojo cuando el programador edite el código fuente. Otro enfoque sería representar de alguna manera lo que está sucediendo en los programas en ejecución. Esto sería un gran acierto especialmente en las aplicaciones basadas en servidores, donde tienes muchos programas en ejecución para mirar. Un perfilador activo podría mostrar gráficamente lo que está sucediendo en la memoria mientras se ejecuta un programa, o incluso hacer sonidos que indiquen lo que está sucediendo.
El sonido es una buena señal de problemas. En un lugar donde trabajé, teníamos un gran tablero de indicadores que mostraba lo que le estaba sucediendo a nuestros servidores web. Las agujas se movían mediante pequeños servomotores que hacían un ligero ruido cuando giraban. No podía ver el tablero desde mi escritorio, pero descubrí que podía saber de inmediato, por el sonido, cuando había un problema con un servidor.
Podría ser posible incluso escribir un perfilador que detectara automáticamente algoritmos ineficientes. No me sorprendería que ciertos patrones de acceso a la memoria resultaran ser señales seguras de malos algoritmos. Si hubiera un pequeño tipo corriendo dentro de la computadora ejecutando nuestros programas, probablemente tendría una historia tan larga y lamentable que contar sobre su trabajo como un empleado del gobierno federal. A menudo tengo la sensación de que estoy enviando al procesador a muchas persecuciones de gansos, pero nunca he tenido una buena manera de ver lo que está haciendo.
Varios Lisps ahora se compilan en código byte, que luego es ejecutado por un intérprete. Esto suele hacerse para facilitar la portabilidad de la implementación, pero podría ser una característica útil del lenguaje. Podría ser una buena idea hacer que el código byte sea una parte oficial del lenguaje y permitir que los programadores usen código byte en línea en los cuellos de botella. Entonces, tales optimizaciones también serían portátiles.
La naturaleza de la velocidad, tal como la percibe el usuario final, puede estar cambiando. Con el auge de las aplicaciones basadas en servidores, cada vez más programas pueden resultar ser limitados por i/o. Valdrá la pena hacer que i/o sea rápido. El lenguaje puede ayudar con medidas sencillas como funciones de salida con formato simple y rápido, y también con cambios estructurales profundos como el almacenamiento en caché y los objetos persistentes.
A los usuarios les interesa el tiempo de respuesta. Pero otro tipo de eficiencia será cada vez más importante: el número de usuarios simultáneos que se pueden admitir por procesador. Muchas de las aplicaciones interesantes que se escribirán en un futuro cercano serán aplicaciones basadas en servidores, y el número de usuarios por servidor es la pregunta crítica para cualquiera que hospede dichas aplicaciones. En el costo de capital de una empresa que ofrece una aplicación basada en servidor, este es el divisor.
Durante años, la eficiencia no ha importado mucho en la mayoría de las aplicaciones para usuarios finales. Los desarrolladores han podido asumir que cada usuario tendría un procesador cada vez más potente en su escritorio. Y por la Ley de Parkinson, el software se ha expandido para usar los recursos disponibles. Eso cambiará con las aplicaciones basadas en servidores. En ese mundo, el hardware y el software se suministrarán juntos. Para las empresas que ofrecen aplicaciones basadas en servidores, hará una gran diferencia en los resultados el número de usuarios que puedan admitir por servidor.
En algunas aplicaciones, el procesador será el factor limitante, y la velocidad de ejecución será lo más importante a optimizar. Pero a menudo la memoria será el límite; el número de usuarios simultáneos estará determinado por la cantidad de memoria que se necesita para los datos de cada usuario. El lenguaje también puede ayudar aquí. Un buen soporte para subprocesos permitirá que todos los usuarios compartan un solo montón. También puede ayudar tener objetos persistentes y/o soporte a nivel de lenguaje para carga diferida.
9 Tiempo
El último ingrediente que necesita un lenguaje de programación popular es el tiempo. Nadie quiere escribir programas en un lenguaje que podría desaparecer, como lo hacen tantos lenguajes de programación. Así que la mayoría de los hackers tenderán a esperar hasta que un lenguaje haya estado en uso durante un par de años antes siquiera de considerar usarlo.
Los inventores de cosas nuevas y maravillosas a menudo se sorprenden al descubrir esto, pero necesitas tiempo para transmitir cualquier mensaje a las personas. Un amigo mío rara vez hace algo la primera vez que alguien se lo pide. Sabe que a veces la gente pide cosas que resulta que no quieren. Para evitar perder su tiempo, espera hasta la tercera o cuarta vez que le piden hacer algo; para entonces, quien sea que le esté pidiendo algo probablemente ya esté bastante molesto, pero al menos probablemente realmente quieran lo que sea que le estén pidiendo.
La mayoría de las personas han aprendido a hacer un tipo de filtrado similar sobre las cosas nuevas que escuchan. No comienzan a prestar atención hasta que han escuchado sobre algo diez veces. Están perfectamente justificados: la mayoría de los nuevos "lo que sea" resultan ser una pérdida de tiempo y eventualmente desaparecen. Al demorar el aprendizaje de VRML, evité tener que aprenderlo en absoluto.
Así que cualquiera que invente algo nuevo tiene que esperar repetir su mensaje durante años antes de que la gente comience a entenderlo. Escribimos lo que, que yo sepa, fue la primera aplicación basada en un servidor web, y nos llevó años hacer que la gente entendiera que no tenía que descargarse. No es que fueran estúpidos. Simplemente nos habían sintonizado.
La buena noticia es que la simple repetición resuelve el problema. Todo lo que tienes que hacer es seguir contando tu historia, y eventualmente la gente comenzará a escuchar. No es cuando la gente se da cuenta de que estás allí cuando prestan atención; es cuando se dan cuenta de que todavía estás allí.
Es bueno que generalmente lleve un tiempo ganar impulso. La mayoría de las tecnologías evolucionan bastante incluso después de que se lanzan por primera vez, especialmente los lenguajes de programación. Nada podría ser mejor para una nueva tecnología que unos años de ser utilizada solo por un pequeño número de primeros adoptantes. Los primeros adoptantes son sofisticados y exigentes, y rápidamente eliminan cualquier defecto que quede en tu tecnología. Cuando solo tienes unos pocos usuarios, puedes estar en contacto cercano con todos ellos. Y los primeros adoptantes son indulgentes cuando mejoras tu sistema, incluso si esto causa algunos problemas.
Hay dos formas en que se introduce una nueva tecnología: el método de crecimiento orgánico y el método de gran explosión. El método de crecimiento orgánico está ejemplificado por la clásica startup de garaje con fondos insuficientes. Un par de tipos, trabajando en el anonimato, desarrollan una nueva tecnología. La lanzan sin marketing y al principio solo tienen unos pocos usuarios (fanáticamente devotos). Continúan mejorando la tecnología, y mientras tanto su base de usuarios crece por el boca a boca. Antes de que se den cuenta, son grandes.
El otro enfoque, el método de gran explosión, está ejemplificado por la startup respaldada por capital de riesgo y con mucha publicidad. Se apresuran a desarrollar un producto, lo lanzan con gran publicidad, y de inmediato (esperan) tienen una gran base de usuarios.
En general, los tipos del garaje envidian a los tipos de la gran explosión. Los tipos de la gran explosión son suaves y seguros de sí mismos, y respetados por los capitalistas de riesgo. Pueden permitirse lo mejor de todo, y la campaña de relaciones públicas que rodea el lanzamiento tiene el efecto secundario de convertirlos en celebridades. Los tipos de crecimiento orgánico, sentados en su garaje, se sienten pobres y sin amor. Y sin embargo, creo que a menudo se equivocan al sentir lástima por sí mismos. El crecimiento orgánico parece producir una mejor tecnología y fundadores más ricos que el método de gran explosión. Si miras las tecnologías dominantes de hoy, encontrarás que la mayoría de ellas crecieron orgánicamente.
Este patrón no se aplica solo a las empresas. También lo ves en la investigación patrocinada. Multics y Common Lisp fueron proyectos de gran explosión, y Unix y MacLisp fueron proyectos de crecimiento orgánico.
10 Rediseño
"Lo mejor de la escritura es reescribir", escribió E. B. White. Todos los buenos escritores saben esto, y también es cierto para el software. La parte más importante del diseño es el rediseño. Los lenguajes de programación, especialmente, no se rediseñan lo suficiente.
Para escribir buen software debes mantener simultáneamente dos ideas opuestas en tu cabeza. Necesitas la fe ingenua del joven hacker en sus habilidades y, al mismo tiempo, el escepticismo del veterano. Tienes que poder pensar ¡qué difícil puede ser? con una mitad de tu cerebro mientras piensas nunca funcionará con la otra.
El truco es darse cuenta de que no hay una contradicción real aquí. Tienes que ser optimista y escéptico sobre dos cosas diferentes. Tienes que ser optimista sobre la posibilidad de resolver el problema, pero escéptico sobre el valor de cualquier solución que hayas obtenido hasta ahora.
Las personas que hacen un buen trabajo a menudo piensan que lo que están trabajando no vale nada. Otros ven lo que han hecho y están llenos de asombro, pero el creador está lleno de preocupación. Este patrón no es una coincidencia: es la preocupación la que hizo que el trabajo fuera bueno.
Si puedes mantener el equilibrio entre la esperanza y la preocupación, te impulsarán hacia adelante de la misma manera que tus dos piernas impulsan hacia adelante una bicicleta. En la primera fase del motor de innovación de dos ciclos, trabajas furiosamente en algún problema, inspirado por tu confianza en que podrás resolverlo. En la segunda fase, miras lo que has hecho a la luz fría de la mañana y ves todos sus defectos con mucha claridad. Pero mientras tu espíritu crítico no supere a tu esperanza, podrás mirar tu sistema admitidamente incompleto y pensar: ¿qué tan difícil puede ser llegar al resto del camino?, continuando así el ciclo.
Es complicado mantener equilibrados estos dos elementos. En los jóvenes hackers, predomina el optimismo. Producen algo, están convencidos de que es genial y nunca lo mejoran. En los hackers veteranos, predomina el escepticismo y ni siquiera se atreven a emprender proyectos ambiciosos.
Cualquier cosa que puedas hacer para mantener el ciclo de rediseño en marcha es buena. La prosa se puede reescribir una y otra vez hasta que estés satisfecho con ella. Pero el software, por regla general, no se rediseña lo suficiente. La prosa tiene lectores, pero el software tiene usuarios. Si un escritor reescribe un ensayo, es poco probable que las personas que leyeron la versión anterior se quejen de que sus pensamientos se hayan roto por alguna incompatibilidad recién introducida.
Los usuarios son una espada de doble filo. Pueden ayudarte a mejorar tu lenguaje, pero también pueden disuadirte de mejorarlo. Así que elige cuidadosamente a tus usuarios y sé lento para aumentar su número. Tener usuarios es como la optimización: el curso sabio es retrasarla. Además, como regla general, en cualquier momento dado puedes salirte con más de lo que crees. Introducir cambios es como quitarse una venda: el dolor es un recuerdo casi tan pronto como lo sientes.
Todos saben que no es una buena idea tener un lenguaje diseñado por un comité. Los comités producen un mal diseño. Pero creo que el peor peligro de los comités es que interfieren con el rediseño. Es tanto el trabajo necesario para introducir cambios que nadie quiere molestarse. Lo que decida un comité tiende a permanecer así, incluso si la mayoría de los miembros no les gusta.
Incluso un comité de dos personas obstaculiza el rediseño. Esto ocurre particularmente en las interfaces entre piezas de software escritas por dos personas diferentes. Para cambiar la interfaz, ambos tienen que estar de acuerdo en cambiarla al mismo tiempo. Y así, las interfaces tienden a no cambiar en absoluto, lo cual es un problema porque suelen ser una de las partes más ad hoc de cualquier sistema.
Una posible solución aquí podría ser diseñar sistemas de modo que las interfaces sean horizontales en lugar de verticales, de modo que los módulos siempre sean estratos de abstracción apilados verticalmente. Entonces la interfaz tenderá a ser propiedad de uno de ellos. El nivel inferior de dos niveles será o bien un lenguaje en el que se escriba el superior, en cuyo caso el nivel inferior será el propietario de la interfaz, o bien será un esclavo, en cuyo caso la interfaz puede ser dictada por el nivel superior.
11 Lisp
Todo esto implica que hay esperanza para un nuevo Lisp. Hay esperanza para cualquier lenguaje que le dé a los hackers lo que quieren, incluido Lisp. Creo que nos hemos equivocado al pensar que a los hackers les molesta la extrañeza de Lisp. Esta ilusión reconfortante puede habernos impedido ver el problema real con Lisp, o al menos con Common Lisp, que es que apesta para hacer lo que los hackers quieren hacer. El lenguaje de un hacker necesita bibliotecas poderosas y algo que hackear. Common Lisp no tiene ninguno de los dos. El lenguaje de un hacker es conciso y hackeable. Common Lisp no lo es.
La buena noticia es que no es Lisp lo que apesta, sino Common Lisp. Si podemos desarrollar un nuevo Lisp que sea un verdadero lenguaje de hacker, creo que los hackers lo usarán. Usarán cualquier lenguaje que haga el trabajo. Todo lo que tenemos que hacer es asegurarnos de que este nuevo Lisp haga mejor que otros lenguajes algún trabajo importante.
La historia ofrece cierto aliento. Con el tiempo, los nuevos lenguajes de programación sucesivos han tomado cada vez más características de Lisp. Ya no queda mucho por copiar antes de que el lenguaje que hayas creado sea Lisp. El último lenguaje de moda, Python, es un Lisp aguado con sintaxis infija y sin macros. Un nuevo Lisp sería un paso natural en esta progresión.
A veces pienso que sería un buen truco de marketing llamarlo una versión mejorada de Python. Eso suena más moderno que Lisp. Para muchas personas, Lisp es un lenguaje lento de IA con muchos paréntesis. La biografía oficial de Fritz Kunze evita cuidadosamente mencionar la palabra L. Pero mi conjetura es que no deberíamos tener miedo de llamar al nuevo Lisp Lisp. Lisp todavía tiene mucho respeto latente entre los mejores hackers, los que tomaron 6.001 y lo entendieron, por ejemplo. Y esos son los usuarios que necesitas ganar.
En "Cómo convertirse en un hacker", Eric Raymond describe a Lisp como algo así como el latín o el griego: un lenguaje que debes aprender como un ejercicio intelectual, aunque en realidad no lo uses mucho:
Lisp vale la pena aprenderlo por la profunda experiencia de iluminación que tendrás cuando finalmente lo entiendas; esa experiencia te hará un mejor programador por el resto de tus días, incluso si en realidad no usas mucho Lisp.
Si no supiera Lisp, leer esto me haría hacer preguntas. Un lenguaje que me haría un mejor programador, si significa algo, significa un lenguaje que sería mejor para programar. Y esa es de hecho la implicación de lo que está diciendo Eric.
Mientras esa idea siga flotando, creo que los hackers serán lo suficientemente receptivos a un nuevo Lisp, incluso si se llama Lisp. Pero este Lisp debe ser un lenguaje de hacker, como los Lisp clásicos de la década de 1970. Debe ser conciso, simple y hackeable. Y debe tener bibliotecas poderosas para hacer lo que los hackers quieren hacer ahora.
En el asunto de las bibliotecas, creo que hay espacio para superar a los lenguajes como Perl y Python en su propio juego. Muchas de las nuevas aplicaciones que deberán escribirse en los próximos años serán aplicaciones basadas en el servidor. No hay razón por la que un nuevo Lisp no deba tener bibliotecas de cadenas tan buenas como Perl, y si este nuevo Lisp también tuviera poderosas bibliotecas para aplicaciones basadas en el servidor, podría ser muy popular. Los hackers reales no mirarán con desdén una nueva herramienta que les permita resolver problemas difíciles con unas pocas llamadas a la biblioteca. Recuerda, los hackers son perezosos.
Podría ser un triunfo aún mayor tener soporte central del lenguaje para aplicaciones basadas en el servidor. Por ejemplo, soporte explícito para programas con múltiples usuarios o propiedad de datos a nivel de etiquetas de tipo.
Las aplicaciones basadas en el servidor también nos dan la respuesta a la pregunta de qué se usará este nuevo Lisp para hackear. No estaría de más mejorar a Lisp como lenguaje de scripting para Unix. (Sería difícil empeorarlo). Pero creo que hay áreas donde los lenguajes existentes serían más fáciles de superar. Creo que podría ser mejor seguir el modelo de Tcl y suministrar Lisp junto con un sistema completo para apoyar aplicaciones basadas en el servidor. Lisp es un ajuste natural para las aplicaciones basadas en el servidor. Los cierres léxicos proporcionan una forma de obtener el efecto de las subrutinas cuando la interfaz de usuario es simplemente una serie de páginas web. Las expresiones S se asignan bien a HTML y las macros son buenas para generarlo. Necesitan haber mejores herramientas para escribir aplicaciones basadas en el servidor, y tiene que haber un nuevo Lisp, y los dos funcionarían muy bien juntos.
12 El lenguaje soñado
A modo de resumen, intentemos describir el lenguaje soñado del hacker. El lenguaje soñado es hermoso, limpio y conciso. Tiene un intérprete interactivo que se inicia rápidamente. Puedes escribir programas para resolver problemas comunes con muy poco código. Casi todo el código en cualquier programa que escribas es código específico de tu aplicación. Todo lo demás se ha hecho por ti.
La sintaxis del lenguaje es breve hasta la falta. Nunca tienes que escribir un carácter innecesario, ni siquiera usar mucho la tecla mayúsculas.
Usando grandes abstracciones, puedes escribir la primera versión de un programa muy rápidamente. Más tarde, cuando quieras optimizar, hay un perfilador realmente bueno que te dice dónde enfocar tu atención. Puedes hacer que los bucles internos sean increíblemente rápidos, incluso escribiendo código de bytes en línea si lo necesitas.
Hay muchos buenos ejemplos de los que aprender, y el lenguaje es lo suficientemente intuitivo como para que puedas aprender a usarlo a partir de ejemplos en un par de minutos. No necesitas consultar el manual a menudo. El manual es delgado y tiene pocas advertencias y calificaciones.
El lenguaje tiene un núcleo pequeño y bibliotecas poderosas y altamente ortogonales que están diseñadas con el mismo cuidado que el lenguaje central. Las bibliotecas funcionan bien entre sí; todo en el lenguaje encaja como las piezas de una cámara fina. Nada está obsoleto o se conserva por compatibilidad. El código fuente de todas las bibliotecas está fácilmente disponible. Es fácil comunicarse con el sistema operativo y con aplicaciones escritas en otros lenguajes.
El lenguaje se construye en capas. Las abstracciones de alto nivel se construyen de una manera muy transparente a partir de abstracciones de nivel más bajo, a las que puedes acceder si lo deseas.
No se te oculta nada que no tenga que ocultarse absolutamente. El lenguaje ofrece abstracciones solo como una forma de ahorrarte trabajo, en lugar de como una forma de decirte qué hacer. De hecho, el lenguaje te anima a ser un participante igual en su diseño. Puedes cambiar todo sobre él, incluyendo incluso su sintaxis, y cualquier cosa que escribas tiene, en la medida de lo posible, el mismo estatus que lo que viene predefinido.
Notas
[1] Las macros muy cercanas a la idea moderna fueron propuestas por Timothy Hart en 1964, dos años después de que se lanzara Lisp 1.5. Lo que faltaba inicialmente eran formas de evitar la captura de variables y la evaluación múltiple; los ejemplos de Hart están sujetos a ambos.
[2] En When the Air Hits Your Brain, el neurocirujano Frank Vertosick relata una conversación en la que su residente jefe, Gary, habla sobre la diferencia entre los cirujanos y los internistas ("pulgas"):
Gary y yo pedimos una pizza grande y encontramos un puesto abierto. El jefe encendió un cigarrillo. "Mira a esos malditos pulgas, parloteando sobre alguna enfermedad que verán una vez en sus vidas. Ese es el problema con las pulgas, solo les gusta el material extraño. Odian sus casos de pan y mantequilla. Esa es la diferencia entre nosotros y las malditas pulgas. Mira, a nosotros nos encantan las grandes y jugosas hernias de disco lumbares, pero a ellos les odian la hipertensión..."
Es difícil pensar en una hernia de disco lumbar como jugosa (excepto literalmente). Y sin embargo, creo que sé a qué se refieren. A menudo he tenido un bicho jugoso para rastrear. Alguien que no es programador encontraría difícil imaginar que pudiera haber placer en un error. Seguramente es mejor si todo simplemente funciona. En cierto modo, lo es. Y sin embargo, hay una innegable satisfacción sombría en cazar cierto tipo de errores.