Cinco preguntas sobre el diseño de lenguajes
Mayo de 2001
(Estas son algunas notas que tomé para una mesa redonda sobre el diseño de lenguajes de programación en el MIT el 10 de mayo de 2001.)
1. Los lenguajes de programación son para personas.
Los lenguajes de programación son la forma en que las personas hablan con las computadoras. A la computadora le daría lo mismo hablar cualquier idioma que no fuera ambiguo. La razón por la que tenemos lenguajes de alto nivel es porque las personas no pueden manejar el lenguaje de máquina. El propósito de los lenguajes de programación es evitar que nuestros pobres y frágiles cerebros humanos se vean abrumados por una masa de detalles.
Los arquitectos saben que algunos tipos de problemas de diseño son más personales que otros. Uno de los problemas de diseño más limpios y abstractos es el diseño de puentes. Allí, tu trabajo es en gran medida una cuestión de salvar una distancia dada con el mínimo material. El otro extremo del espectro es el diseño de sillas. Los diseñadores de sillas tienen que pasar su tiempo pensando en los traseros humanos.
El software varía de la misma manera. Diseñar algoritmos para enrutar datos a través de una red es un problema agradable y abstracto, como diseñar puentes. Mientras que diseñar lenguajes de programación es como diseñar sillas: se trata de lidiar con las debilidades humanas.
La mayoría de nosotros odiamos admitir esto. Diseñar sistemas de gran elegancia matemática suena mucho más atractivo para la mayoría de nosotros que complacer las debilidades humanas. Y hay un papel para la elegancia matemática: algunos tipos de elegancia hacen que los programas sean más fáciles de entender. Pero la elegancia no es un fin en sí misma.
Y cuando digo que los lenguajes deben diseñarse para adaptarse a las debilidades humanas, no quiero decir que los lenguajes deban diseñarse para programadores malos. De hecho, creo que deberías diseñar para los mejores programadores, pero incluso los mejores programadores tienen limitaciones. No creo que a nadie le gustara programar en un lenguaje donde todas las variables fueran la letra x con subíndices enteros.
2. Diseña para ti y tus amigos.
Si miras la historia de los lenguajes de programación, muchos de los mejores fueron lenguajes diseñados para que los usaran sus propios autores, y muchos de los peores fueron diseñados para que los usaran otras personas.
Cuando los lenguajes se diseñan para otras personas, siempre es para un grupo específico de otras personas: personas no tan inteligentes como el diseñador del lenguaje. Así que obtienes un lenguaje que te habla condescendientemente. Cobol es el caso más extremo, pero muchos lenguajes están impregnados de este espíritu.
No tiene nada que ver con cuán abstracto es el lenguaje. C es bastante de bajo nivel, pero fue diseñado para que lo usaran sus autores, y por eso a los hackers les gusta.
El argumento para diseñar lenguajes para programadores malos es que hay más programadores malos que buenos. Eso puede ser cierto. Pero esos pocos buenos programadores escriben un porcentaje desproporcionadamente grande del software.
Me interesa la pregunta: ¿cómo se diseña un lenguaje que les guste a los mejores hackers? Me da la impresión de que esto es idéntico a la pregunta: ¿cómo se diseña un buen lenguaje de programación?, pero incluso si no lo es, al menos es una pregunta interesante.
3. Dale al programador tanto control como sea posible.
Muchos lenguajes (especialmente los diseñados para otras personas) tienen la actitud de una institutriz: intentan evitar que hagas cosas que creen que no son buenas para ti. Me gusta el enfoque opuesto: dale al programador tanto control como puedas.
Cuando aprendí Lisp por primera vez, lo que más me gustó fue que me consideraba un socio igualitario. En los otros lenguajes que había aprendido hasta entonces, estaba el lenguaje y estaba mi programa, escrito en el lenguaje, y los dos estaban muy separados. Pero en Lisp, las funciones y macros que escribía eran como las que componían el lenguaje mismo. Podía reescribir el lenguaje si quería. Tenía el mismo atractivo que el software de código abierto.
4. Apunta a la brevedad.
La brevedad es subestimada e incluso despreciada. Pero si miras en los corazones de los hackers, verás que realmente la aman. ¿Cuántas veces has oído a los hackers hablar con cariño de cómo, digamos, en APL, podían hacer cosas increíbles con solo un par de líneas de código? Creo que cualquier cosa que a las personas realmente inteligentes les encante de verdad vale la pena prestarle atención.
Creo que casi todo lo que puedas hacer para acortar los programas es bueno. Debería haber muchas funciones de biblioteca; todo lo que pueda ser implícito debería serlo; la sintaxis debería ser concisa hasta el extremo; incluso los nombres de las cosas deberían ser cortos.
Y no solo los programas deben ser cortos. El manual también debe ser delgado. Una buena parte de los manuales se dedica a aclaraciones, reservas y advertencias, y casos especiales. Si te obligas a acortar el manual, en el mejor de los casos lo haces arreglando las cosas en el lenguaje que requerían tanta explicación.
5. Admite qué es el hacking.
A mucha gente le gustaría que el hacking fuera matemáticas, o al menos algo parecido a una ciencia natural. Creo que el hacking se parece más a la arquitectura. La arquitectura está relacionada con la física, en el sentido de que los arquitectos tienen que diseñar edificios que no se caigan, pero el objetivo real de los arquitectos es hacer grandes edificios, no hacer descubrimientos sobre estática.
Lo que les gusta hacer a los hackers es crear grandes programas. Y creo que, al menos en nuestras propias mentes, debemos recordar que escribir grandes programas es algo admirable, incluso cuando este trabajo no se traduce fácilmente a la moneda intelectual convencional de los artículos de investigación. Intelectualmente, es tan valioso diseñar un lenguaje que los programadores amarán como diseñar uno horrible que encarne alguna idea sobre la que puedas publicar un artículo.
1. ¿Cómo organizar grandes bibliotecas?
Las bibliotecas se están convirtiendo en un componente cada vez más importante de los lenguajes de programación. También se están haciendo más grandes, y esto puede ser peligroso. Si se tarda más en encontrar la función de biblioteca que hará lo que quieres que en escribirla tú mismo, entonces todo ese código no hace más que hacer que tu manual sea grueso. (Los manuales de Symbolics fueron un caso en cuestión.) Así que creo que tendremos que trabajar en formas de organizar las bibliotecas. Lo ideal sería diseñarlas de manera que el programador pudiera adivinar qué llamada de biblioteca haría lo correcto.
2. ¿La gente realmente tiene miedo a la sintaxis prefija?
Este es un problema abierto en el sentido de que lo he pensado durante años y todavía no sé la respuesta. La sintaxis prefija me parece perfectamente natural, excepto quizás para las matemáticas. Pero podría ser que gran parte de la impopularidad de Lisp se deba simplemente a tener una sintaxis desconocida. Si hay que hacer algo al respecto, si es cierto, es otra cuestión.
3. ¿Qué necesitas para el software basado en servidor?
Creo que muchas de las nuevas aplicaciones más emocionantes que se escribirán en los próximos veinte años serán aplicaciones basadas en la Web, es decir, programas que residen en el servidor y se comunican contigo a través de un navegador web. Y para escribir este tipo de programas, es posible que necesitemos algunas cosas nuevas.
Una cosa que necesitaremos es soporte para la nueva forma en que se lanzan las aplicaciones basadas en servidor. En lugar de tener uno o dos lanzamientos grandes al año, como el software de escritorio, las aplicaciones basadas en servidor se lanzan como una serie de pequeños cambios. Puedes tener hasta cinco o diez lanzamientos al día. Y, por regla general, todo el mundo utilizará siempre la última versión.
¿Sabes cómo puedes diseñar programas para que sean depurables? Bueno, el software basado en servidor, de manera similar, debe diseñarse para ser modificable. Debes poder cambiarlo fácilmente, o al menos saber qué es un cambio pequeño y qué es uno trascendental.
Otra cosa que podría resultar útil para el software basado en servidor, sorprendentemente, son las continuaciones. En el software basado en la Web, puedes usar algo como el estilo de paso de continuaciones para obtener el efecto de subrutinas en el mundo inherentemente sin estado de una sesión web. Quizás valdría la pena tener continuaciones reales, si no fuera demasiado costoso.
4. ¿Qué nuevas abstracciones quedan por descubrir?
No estoy seguro de cuán razonable es esta esperanza, pero una cosa que me encantaría hacer personalmente es descubrir una nueva abstracción, algo que marque una diferencia tan grande como tener funciones de primera clase, recursión o incluso parámetros con nombre. Este puede ser un sueño imposible. Estas cosas no se descubren con mucha frecuencia. Pero siempre estoy buscando.
1. Puedes usar el lenguaje que quieras.
Escribir programas de aplicación solía significar escribir software de escritorio. Y en el software de escritorio hay un gran sesgo hacia escribir la aplicación en el mismo lenguaje que el sistema operativo. Y así, hace diez años, escribir software significaba, en gran medida, escribir software en C. Finalmente, evolucionó una tradición: los programas de aplicación no deben escribirse en lenguajes inusuales. Y esta tradición tuvo tanto tiempo para desarrollarse que personas no técnicas como gerentes y capitalistas de riesgo también la aprendieron.
El software basado en servidor elimina todo este modelo. Con el software basado en servidor, puedes usar cualquier lenguaje que quieras. Casi nadie entiende esto todavía (especialmente los gerentes y capitalistas de riesgo). Algunos hackers lo entienden, y por eso oímos hablar de lenguajes nuevos e independientes como Perl y Python. No oímos hablar de Perl y Python porque la gente los esté usando para escribir aplicaciones de Windows.
Lo que esto significa para nosotros, como personas interesadas en diseñar lenguajes de programación, es que ahora hay potencialmente una audiencia real para nuestro trabajo.
2. La velocidad proviene de los perfiladores.
A los diseñadores de lenguajes, o al menos a los implementadores de lenguajes, les gusta escribir compiladores que generan código rápido. Pero no creo que esto sea lo que hace que los lenguajes sean rápidos para los usuarios. Knuth señaló hace mucho tiempo que la velocidad solo importa en unos pocos cuellos de botella críticos. Y cualquiera que lo haya intentado sabe que no se puede adivinar dónde están estos cuellos de botella. Los perfiladores son la respuesta.
Los diseñadores de lenguajes están resolviendo el problema equivocado. Los usuarios no necesitan puntos de referencia para que funcionen rápido. Lo que necesitan es un lenguaje que pueda mostrarles qué partes de sus propios programas necesitan ser reescritas. Ahí es de donde proviene la velocidad en la práctica. Así que tal vez sería una ganancia neta si los implementadores de lenguajes dedicaran la mitad del tiempo que habrían dedicado a optimizaciones de compiladores a escribir un buen perfilador en su lugar.
3. Necesitas una aplicación para impulsar el diseño de un lenguaje.
Esto puede que no sea una regla absoluta, pero parece que los mejores lenguajes evolucionaron junto con alguna aplicación para la que se estaban utilizando. C fue escrito por personas que lo necesitaban para la programación de sistemas. Lisp se desarrolló en parte para hacer diferenciación simbólica, y McCarthy estaba tan ansioso por empezar que escribía programas de diferenciación incluso en el primer artículo sobre Lisp, en 1960.
Es especialmente bueno si tu aplicación resuelve algún problema nuevo. Eso tenderá a impulsar tu lenguaje a tener nuevas características que los programadores necesitan. Personalmente, estoy interesado en escribir un lenguaje que sea bueno para escribir aplicaciones basadas en servidor.
[Durante la mesa redonda, Guy Steele también hizo este punto, con la sugerencia adicional de que la aplicación no debe consistir en escribir el compilador de tu lenguaje, a menos que tu lenguaje esté destinado a escribir compiladores.]
4. Un lenguaje tiene que ser bueno para escribir programas desechables.
Sabes lo que es un programa desechable: algo que escribes rápidamente para una tarea limitada. Creo que si miraras a tu alrededor, encontrarías que muchos programas grandes y serios comenzaron como programas desechables. No me sorprendería si la mayoría de los programas comenzaran como programas desechables. Y así, si quieres hacer un lenguaje que sea bueno para escribir software en general, tiene que ser bueno para escribir programas desechables, porque esa es la etapa larvaria de la mayoría del software.
5. La sintaxis está conectada con la semántica.
Es tradicional pensar que la sintaxis y la semántica están completamente separadas. Esto sonará impactante, pero puede que no sea así. Creo que lo que quieres en tu lenguaje puede estar relacionado con cómo lo expresas.
Estaba hablando recientemente con Robert Morris, y señaló que la sobrecarga de operadores es una gran ventaja en lenguajes con sintaxis infija. En un lenguaje con sintaxis prefija, cualquier función que definas es efectivamente un operador. Si quieres definir un más para un nuevo tipo de número que has inventado, puedes simplemente definir una nueva función para sumarlos. Si haces eso en un lenguaje con sintaxis infija, hay una gran diferencia en la apariencia entre el uso de un operador sobrecargado y una llamada a función.
1. Nuevos lenguajes de programación.
En la década de 1970, estaba de moda diseñar nuevos lenguajes de programación. Recientemente no lo ha estado. Pero creo que el software basado en servidor hará que los nuevos lenguajes vuelvan a estar de moda. Con el software basado en servidor, puedes usar cualquier lenguaje que quieras, por lo que si alguien diseña un lenguaje que realmente parece mejor que otros disponibles, habrá personas que asuman el riesgo y lo utilicen.
2. Tiempo compartido.
Richard Kelsey lo mencionó como una idea cuya hora ha vuelto en la última mesa redonda, y estoy completamente de acuerdo con él. Mi suposición (y la de Microsoft, al parecer) es que gran parte de la computación se trasladará del escritorio a servidores remotos. En otras palabras, el tiempo compartido ha vuelto. Y creo que necesitará soporte a nivel de lenguaje. Por ejemplo, sé que Richard y Jonathan Rees han hecho mucho trabajo implementando la planificación de procesos dentro de Scheme 48.
3. Eficiencia.
Recientemente, empezaba a parecer que las computadoras eran finalmente lo suficientemente rápidas. Cada vez más oíamos hablar de bytecode, lo que me implica al menos que sentimos que tenemos ciclos de sobra. Pero no creo que los tengamos, con el software basado en servidor. Alguien tendrá que pagar por los servidores en los que se ejecuta el software, y el número de usuarios que pueden soportar por máquina será el divisor de su costo de capital.
Así que creo que la eficiencia importará, al menos en los cuellos de botella computacionales. Será especialmente importante hacer i/o rápido, porque las aplicaciones basadas en servidor hacen mucho i/o.
Puede resultar que el bytecode no sea una ventaja, al final. Sun y Microsoft parecen estar enfrentándose en una especie de batalla de los bytecodes en este momento. Pero lo hacen porque el bytecode es un lugar conveniente para insertarse en el proceso, no porque el bytecode sea en sí mismo una buena idea. Puede resultar que todo este campo de batalla sea evitado. Eso sería algo divertido.
1. Clientes.
Esta es solo una suposición, pero mi suposición es que el modelo ganador para la mayoría de las aplicaciones será puramente basado en servidor. Diseñar software que funcione bajo la suposición de que todos tendrán tu cliente es como diseñar una sociedad bajo la suposición de que todos serán honestos. Ciertamente sería conveniente, pero tienes que asumir que nunca sucederá.
Creo que habrá una proliferación de dispositivos que tendrán algún tipo de acceso web, y todo lo que podrás asumir sobre ellos es que pueden soportar HTML simple y formularios. ¿Tendrás un navegador en tu teléfono móvil? ¿Habrá un teléfono en tu Palm Pilot? ¿Obtendrá tu Blackberry una pantalla más grande? ¿Podrás navegar por la web en tu Gameboy? ¿Tu reloj? No lo sé. Y no tengo que saberlo si apuesto a que todo estará solo en el servidor. Es mucho más robusto tener todo el cerebro en el servidor.
2. Programación Orientada a Objetos.
Me doy cuenta de que esto es controvertido, pero no creo que la programación orientada a objetos sea algo tan importante. Creo que es un buen modelo para ciertos tipos de aplicaciones que necesitan ese tipo específico de estructura de datos, como sistemas de ventanas, simulaciones y programas CAD. Pero no veo por qué debería ser el modelo para toda la programación.
Creo que parte de la razón por la que a las personas en grandes empresas les gusta la programación orientada a objetos es porque produce mucho de lo que parece trabajo. Algo que podría representarse naturalmente como, digamos, una lista de enteros, ahora puede representarse como una clase con todo tipo de estructura de soporte, ajetreo y bullicio.
Otra atracción de la programación orientada a objetos es que los métodos te dan parte del efecto de las funciones de primera clase. Pero esto es noticia vieja para los programadores de Lisp. Cuando tienes funciones de primera clase reales, puedes usarlas de la manera que sea apropiada para la tarea en cuestión, en lugar de forzar todo en un molde de clases y métodos.
Lo que esto significa para el diseño de lenguajes, creo, es que no deberías integrar la programación orientada a objetos demasiado profundamente. Quizás la respuesta sea ofrecer cosas más generales y subyacentes, y dejar que las personas diseñen los sistemas de objetos que quieran como bibliotecas.
3. Diseño por comité.
Que tu lenguaje sea diseñado por un comité es una gran trampa, y no solo por las razones que todos conocen. Todos saben que los comités tienden a producir diseños irregulares e inconsistentes. Pero creo que un peligro mayor es que no asumirán riesgos. Cuando una persona está a cargo, puede asumir riesgos que un comité nunca aprobaría.
¿Es necesario asumir riesgos para diseñar un buen lenguaje? Muchas personas podrían sospechar que el diseño de lenguajes es algo en lo que deberías ceñirte bastante a la sabiduría convencional. Apuesto a que esto no es cierto. En todo lo demás que hacen las personas, la recompensa es proporcional al riesgo. ¿Por qué el diseño de lenguajes debería ser diferente?