Loading...

SER POPULAR

Original

Mayo 2001

(Este artículo fue escrito como una especie de plan de negocios para un nuevo lenguaje. Así que le falta (porque 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 basándose en sus méritos, y que no importaba cuán bueno fuera su lenguaje, nadie lo usaría. Al menos, eso era lo que había sucedido con el lenguaje que él había diseñado.

¿Qué 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 al observar a los hackers y aprender 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 lenguajes de programación simplemente basándose en sus méritos. La mayoría de los programadores son instruidos sobre qué lenguaje usar por alguien más. Y, sin embargo, creo que el efecto de tales factores externos sobre la popularidad de los lenguajes de programación no es tan grande como a veces se piensa. Creo que un problema mayor es que la idea de un buen lenguaje de programación para un hacker no es la misma que para 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 personas, y deben ser diseñadas para adaptarse a las fortalezas y debilidades humanas tanto como los zapatos deben ser diseñados para los pies humanos. Si un zapato aprieta cuando te lo pones, es un mal zapato, por muy elegante que sea 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 pequeña minoría, es cierto, pero esa pequeña minoría escribe todo el buen software, y su influencia es tal que el resto de los programadores tenderá a usar cualquier lenguaje que ellos usen. A menudo, de hecho, no es meramente influencia, sino mando: a menudo los hackers expertos son las mismas personas que, como sus jefes o asesores académicos, dicen a los otros programadores qué lenguaje usar.

La opinión de los hackers expertos no es la única fuerza que determina la popularidad relativa de los lenguajes de programación — el software heredado (Cobol) y la publicidad (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 tan popular como merece ser. Y la popularidad separa aún más los buenos lenguajes de los malos, porque la retroalimentación de los 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; estas evolucionaron más tarde, después de que los hackers del MIT pasaron 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, 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 queda quieto. Y, sin embargo, los Lisps que tenemos hoy son todavía bastante 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 suficientemente grande y exigente.

Por supuesto, los hackers tienen que conocer un lenguaje antes de poder usarlo. ¿Cómo se enteran? De otros hackers. Pero tiene que haber algún grupo inicial de hackers usando el lenguaje para que otros incluso escuchen sobre él. Me pregunto cuán grande tiene que ser este grupo; ¿cuántos usuarios hacen una masa crítica? A bote pronto, diría veinte. Si un lenguaje tuviera veinte usuarios separados, es decir, veinte usuarios que decidieron por su cuenta usarlo, lo consideraría real.

Llegar allí no puede ser fácil. No me sorprendería si es más difícil pasar de cero a veinte que de veinte a mil. La mejor manera de conseguir esos primeros veinte usuarios es probablemente usar un caballo de Troya: dar a las personas una aplicación que quieren, que resulta estar escrita en el nuevo lenguaje.

2 Factores Externos

Comencemos reconociendo 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 las primeras computadoras IBM. C fue el lenguaje de scripting de Unix, y así, más tarde, 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 retiene 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 estaban 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 pocos 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ía de la programación en Lisp que se hace hoy se realiza en Emacs Lisp o AutoLisp.

Los lenguajes de programación no existen en aislamiento. 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 utilizan para hackear. Así que si quieres diseñar un lenguaje popular, o bien tienes que proporcionar más que un lenguaje, o bien tienes que diseñar tu lenguaje para reemplazar el lenguaje de scripting de algún sistema existente.

Common Lisp es impopular 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 poder creciente de los procesadores de propósito general en la década de 1980. Common Lisp podría haber permanecido popular si hubiera sido un buen lenguaje de scripting para Unix. Es, lamentablemente, un lenguaje atrocemente 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 scripting de algo. Esto solo parece injusto si llega como una sorpresa. Creo que no es más injusto que esperar que un lenguaje de programación tenga, digamos, una implementación. Es simplemente parte de lo que es un lenguaje de programación.

Un lenguaje de programación necesita una buena implementación, por supuesto, y esta debe ser gratuita. Las empresas pagarán por 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 que importa para los hackers.

También debería 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 obsoletos 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 puedes proporcionar las tres cosas que necesita cualquier lenguaje: una implementación gratuita, un libro y algo para hackear, ¿cómo haces 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 cualquier cosa superflua. No estaría lejos de la verdad decir que un hacker a punto de escribir un programa decide qué lenguaje usar, al menos subconscientemente, basándose en el número total de caracteres que tendrá que escribir. Si esto no es precisamente cómo piensan los hackers, un diseñador de lenguajes haría bien en actuar como si lo fuera.

Es un error intentar mimar al usuario con expresiones largas que están destinadas a parecerse al inglés. Cobol es notorio por este defecto. Un hacker consideraría que se le pida escribir

add x to y giving z

en lugar de

z = x+y

es algo entre un insulto a su inteligencia y un pecado contra Dios.

A veces se ha dicho que Lisp debería usar first y rest en lugar de car y cdr, porque haría que los programas fueran más fáciles de leer. Quizás durante las primeras horas. Pero un hacker puede aprender lo suficientemente rápido que car significa el primer elemento de una lista y cdr significa el resto. Usar first y rest significa un 50% más de escritura. Y también son de diferentes longitudes, lo que significa que los argumentos no se alinearán cuando se llamen, como car y cdr a menudo lo son, en líneas sucesivas. He descubierto que importa mucho cómo se alinean las líneas de código en la página. Apenas puedo leer código Lisp cuando está configurado en una fuente de ancho variable, y amigos dicen que esto es cierto para otros lenguajes también.

La brevedad es un lugar donde los lenguajes fuertemente tipados pierden. Con todas las demás cosas siendo iguales, nadie quiere comenzar un programa con un montón de declaraciones. Cualquier cosa que pueda ser implícita, debería serlo.

Los tokens individuales también deberían ser cortos. Perl y Common Lisp ocupan polos opuestos en esta cuestión. Los programas Perl pueden ser casi crípticamente 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 escribirían estos largos nombres por ellos. 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 tu pantalla.

4 Hackeabilidad

Hay una cosa más importante que la brevedad para un hacker: poder hacer lo que quieres. En la historia de los lenguajes de programación se ha invertido una sorprendente cantidad de esfuerzo en prevenir que los programadores hagan cosas consideradas inapropiadas. Este es un plan peligrosamente presuntuoso. ¿Cómo puede el diseñador del lenguaje saber qué va a necesitar hacer el programador? Creo que los diseñadores de lenguajes 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. Puedes salvarlo de referirse a variables en otro paquete, pero no puedes 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 convenientes. Por poco convenientes me refiero a cosas que van más allá 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 meterse dentro de las cosas y adivinar al diseñador original.

Déjate adivinar. Cuando haces cualquier herramienta, las personas la usan de maneras que no pretendías, y esto es especialmente cierto en una herramienta altamente articulada como un lenguaje de programación. Muchos hackers querrán ajustar tu modelo semántico de una manera que nunca imaginaste. Yo digo, déjalos; dale al programador acceso a tanto material interno como puedas sin poner en peligro sistemas de 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 eliminar referencias a un objeto eliminado, por ejemplo, o encontrar campos que no están inicializados. 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 en cualquier estructura. Solo puedo acceder a los campos por nombre, porque eso es lo que se supone que significa una estructura.

Un hacker puede querer subvertir el modelo previsto de las cosas solo una o dos veces en un gran programa. Pero qué diferencia hace poder hacerlo. Y puede ser más que una cuestión de simplemente resolver un problema. También hay una especie de placer aquí. Los hackers comparten el secreto placer del cirujano de hurgar en las entrañas, el secreto placer del adolescente de reventar granos. [2] Para los chicos, al menos, ciertos tipos de horrores son fascinantes. La revista Maxim publica un volumen anual de fotografías, que contiene una mezcla de pin-ups y accidentes espeluznantes. Saben a quién se dirigen.

Históricamente, Lisp ha sido bueno en dejar que los hackers hagan lo que quieran. La corrección política de Common Lisp es una aberración. Los primeros Lisps te dejaban acceder a todo. Una buena parte de ese espíritu se conserva, afortunadamente, en las macros. Qué cosa tan maravillosa, poder hacer transformaciones arbitrarias en el código fuente.

Las macros clásicas son una verdadera herramienta de hacker: simples, poderosas y peligrosas. Es tan fácil entender lo que hacen: llamas a una función con los argumentos de la macro, y lo que sea que devuelva se inserta en lugar de la llamada a la macro. Las macros higiénicas encarnan el principio opuesto. Intentan protegerte de entender lo que están haciendo. Nunca he oído que se expliquen las macros higiénicas en una oración. Y son un ejemplo clásico de los peligros de decidir lo que se permite a los programadores querer. Las macros higiénicas están destinadas a protegerme de la captura de variables, entre otras cosas, pero la captura de variables es exactamente lo que quiero en algunas macros.

Un lenguaje realmente bueno debería ser tanto limpio como sucio: limpiamente diseñado, con un núcleo pequeño de operadores bien entendidos y altamente ortogonales, pero sucio en el sentido de que deja que los hackers hagan lo que quieran con él. C es así. También lo eran los primeros Lisps. Un lenguaje de hacker real siempre tendrá un carácter ligeramente desaliñado.

Un buen lenguaje de programación debería tener características que hagan que el tipo de personas que usan la frase "ingeniería de software" sacudan la cabeza desaprobatoriamente. En el otro extremo del continuo están lenguajes como Ada y Pascal, modelos de propiedad que son buenos para enseñar y no mucho 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 tiene que 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 tiran. Muchos evolucionan hacia programas reales, con características reales y usuarios reales.

Tengo la corazonada de que los mejores programas grandes comienzan la vida de esta manera, en lugar de ser diseñados grandes desde el principio, como la represa Hoover. Es aterrador construir algo grande desde cero. Cuando las personas asumen un proyecto que es demasiado grande, se sienten abrumadas. El proyecto se estanca, o el resultado es estéril y rígido: un centro comercial en lugar de un verdadero centro de la ciudad, Brasilia en lugar de Roma, Ada en lugar de C.

Otra forma de obtener un gran programa es comenzar con un programa desechable y seguir mejorándolo. Este enfoque es menos desalentador, y el diseño del programa se beneficia de la evolución. Creo que, si se mirara, esto resultaría ser la forma en que se desarrollaron la mayoría de los grandes programas. Y aquellos que evolucionaron de esta manera probablemente siguen escritos en el lenguaje en el que fueron escritos por primera vez, porque es raro que un programa sea portado, excepto por razones políticas. Y así, paradójicamente, si quieres hacer un lenguaje que se use para grandes sistemas, tienes que hacerlo bueno para escribir programas desechables, porque de ahí provienen los grandes sistemas.

Perl es un ejemplo notable de esta idea. No solo fue diseñado para escribir programas desechables, sino que fue prácticamente un programa desechable en sí mismo. Perl comenzó como una colección de utilidades para generar informes, y solo evolucionó hacia un lenguaje de programación a medida que los programas desechables que la gente escribía en él crecían en tamaño. No fue hasta Perl 5 (si es que entonces) que el lenguaje fue adecuado para escribir programas serios, y, sin embargo, ya era masivamente popular.

¿Qué hace que un lenguaje sea bueno para 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 debe estar ya instalado en la computadora 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, es más disponible que uno que tienes que compilar y ejecutar por separado. Un lenguaje de programación popular debería 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 último en brevedad es tener el programa ya escrito para ti, y simplemente 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: funciones de biblioteca. Perl gana porque tiene grandes bibliotecas para manipular cadenas. Esta clase de funciones de biblioteca son especialmente importantes para programas desechables, que a menudo se escriben originalmente para convertir o extraer datos. Muchos programas de Perl probablemente comienzan como solo un par de llamadas a la biblioteca unidas entre sí.

Creo que muchos de los avances que ocurrirán en los lenguajes de programación en los próximos cincuenta años tendrán que ver con funciones de biblioteca. Creo que los lenguajes de programación futuros tendrán bibliotecas que están tan cuidadosamente diseñadas como el lenguaje central. El diseño de lenguajes de programación no se tratará de si hacer tu lenguaje fuertemente o débilmente tipado, o 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 puede estremecerse ante esto. ¡Es casi como escribir aplicaciones! Qué pena. Los lenguajes son para programadores, y las bibliotecas son lo que los programadores necesitan.

Es difícil diseñar buenas bibliotecas. No es simplemente una cuestión 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 necesitan ser diseñadas utilizando un pequeño conjunto de operadores ortogonales, al igual que el lenguaje central. Debería ser posible para el programador adivinar qué llamada a la biblioteca hará lo que necesita.

Las bibliotecas son un lugar donde Common Lisp se queda corto. Solo hay bibliotecas rudimentarias para manipular cadenas, y casi ninguna para comunicarse con el sistema operativo. Por razones históricas, Common Lisp intenta pretender que el SO no existe. Y porque no puedes hablar con el SO, es poco probable que puedas escribir un programa serio usando solo los operadores integrados en Common Lisp. También tienes que usar algunos trucos específicos de implementación, y en la práctica tenderán a no darte todo lo que quieres. Los hackers pensarían mucho más positivamente sobre Lisp si Common Lisp tuviera poderosas bibliotecas de cadenas y buen soporte del SO.

7 Sintaxis

¿Podría un lenguaje con la sintaxis de Lisp, o más precisamente, la falta de sintaxis, volverse popular? No sé la respuesta a esta pregunta. Creo que la sintaxis no es la razón principal por la que Lisp no es actualmente popular. Common Lisp tiene problemas peores que una sintaxis poco familiar. Conozco a varios programadores que se sienten cómodos con la sintaxis prefija y, sin embargo, usan Perl por defecto, porque tiene poderosas bibliotecas de cadenas y puede comunicarse con el SO.

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 de Lisp es que el primer problema es el real. No estoy tan seguro. Sí, la notación prefija hace que los programadores ordinarios entren en pánico. Pero no creo que las opiniones de los programadores ordinarios importen. Los lenguajes se vuelven populares o impopulares basándose en lo que piensan los hackers expertos, y creo que los hackers expertos podrían ser capaces de lidiar con la notación prefija. La sintaxis de Perl puede ser bastante incomprensible, pero eso no ha impedido la popularidad de Perl. Si acaso, puede haber ayudado a fomentar un culto a Perl.

Un problema más serio es la difusividad de la notación prefija. Para los hackers expertos, eso realmente es un problema. Nadie quiere escribir (aref a x y) cuando podría escribir a[x,y].

En este caso particular hay una manera de salir del problema. Si tratamos las estructuras de datos como si fueran funciones sobre í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 deshacernos de (o hacer opcionales) muchas 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, así como 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 expresiones matemáticas. He usado Lisp toda mi vida de programación y todavía no encuentro natural las expresiones matemáticas prefijas. Y, sin embargo, es conveniente, especialmente cuando estás generando código, tener operadores que acepten cualquier número de argumentos. Así que si tenemos sintaxis infija, probablemente debería implementarse como algún tipo de macro de lectura.

No creo que debamos estar religiosamente opuestos a introducir sintaxis en Lisp, siempre que se traduzca de una manera bien entendida en expresiones s 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 egregiamente no lispy en Common Lisp ocurre en las cadenas de formato; el formato 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 ser incluidos en él. Sería algo bueno si las macros pudieran generar especificadores de formato de la manera en que generan cualquier otro tipo de código.

Un eminente hacker de Lisp me dijo que su copia de CLTL se abre en la sección de formato. La mía también. Esto probablemente indica un margen de mejora. También puede significar que los programas hacen mucho I/O.

8 Eficiencia

Un buen lenguaje, como todos saben, debería generar código rápido. Pero en la práctica, no creo que el código rápido provenga principalmente de las cosas que haces en el diseño del lenguaje. Como Knuth señaló hace mucho tiempo, la velocidad solo importa en ciertos cuellos de botella críticos. Y como muchos programadores han observado desde entonces, a menudo uno se equivoca sobre dónde están estos cuellos de botella.

Así que, en la práctica, la forma de obtener código rápido es tener un muy buen profiler, en lugar de, digamos, hacer que el lenguaje sea fuertemente tipado. No necesitas conocer el tipo de cada argumento en cada llamada en el 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 ayudaría mucho a 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. Piensan en el profiler como un complemento, en el mejor de los casos. Pero en la práctica, un buen profiler 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í, de nuevo, los diseñadores de lenguajes están algo desconectados de sus usuarios. Hacen un muy buen trabajo al resolver ligeramente el problema equivocado.

Podría ser una buena idea tener un profiler activo: empujar los datos de rendimiento al programador en lugar de esperar a que él venga a pedirlo. Por ejemplo, el editor podría mostrar cuellos de botella en rojo cuando el programador edita 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 especialmente beneficioso en aplicaciones basadas en servidor, donde tienes muchos programas en ejecución para observar. Un profiler activo podría mostrar gráficamente lo que está sucediendo en la memoria mientras un programa se está ejecutando, o incluso hacer sonidos que indiquen lo que está sucediendo.

El sonido es una buena pista para problemas. En un lugar donde trabajé, teníamos un gran tablero de diales que mostraba lo que estaba sucediendo con nuestros servidores web. Las manecillas eran movidas por 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 decir inmediatamente, por el sonido, cuando había un problema con un servidor.

Incluso podría ser posible escribir un profiler que detectara automáticamente algoritmos ineficientes. No me sorprendería si ciertos patrones de acceso a la memoria resultaran ser signos seguros 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 en una serie de búsquedas infructuosas, pero nunca he tenido una buena manera de ver lo que está haciendo.

Varios Lisps ahora se compilan en bytecode, que luego es ejecutado por un intérprete. Esto generalmente se hace 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 bytecode sea una parte oficial del lenguaje, y permitir a los programadores usar bytecode en línea en los cuellos de botella. Entonces tales optimizaciones también serían portables.

La naturaleza de la velocidad, tal como la percibe el usuario final, puede estar cambiando. Con el auge de las aplicaciones basadas en servidor, cada vez más programas pueden resultar ser limitados por I/O. Valdrá la pena hacer que el I/O sea rápido. El lenguaje puede ayudar con medidas sencillas como funciones de salida formateadas simples y rápidas, y también con cambios estructurales profundos como el almacenamiento en caché y objetos persistentes.

Los usuarios están interesados en el tiempo de respuesta. Pero otro tipo de eficiencia será cada vez más importante: el número de usuarios simultáneos que puedes soportar por procesador. Muchas de las aplicaciones interesantes escritas en el futuro cercano serán basadas en servidor, y el número de usuarios por servidor es la pregunta crítica para cualquiera que aloje tales aplicaciones. En el costo de capital de un negocio 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 el usuario final. Los desarrolladores han podido asumir que cada usuario tendría un procesador cada vez más poderoso en su escritorio. Y por la Ley de Parkinson, el software se ha expandido para utilizar los recursos disponibles. Eso cambiará con las aplicaciones basadas en servidor. En ese mundo, el hardware y el software se suministrarán juntos. Para las empresas que ofrecen aplicaciones basadas en servidor, hará una gran diferencia en el resultado final cuántos usuarios pueden soportar 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 se determinará por la cantidad de memoria que necesitas para los datos de cada usuario. El lenguaje puede ayudar aquí también. Un buen soporte para hilos 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 popular es 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 presente durante un par de años antes de considerar incluso usarlo.

Los inventores de cosas 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 las personas a veces piden cosas que resultan no querer. Para evitar perder su tiempo, espera hasta la tercera o cuarta vez que le piden hacer algo; para entonces, quien le está pidiendo puede estar bastante molesto, pero al menos probablemente realmente quieren lo que están pidiendo.

La mayoría de las personas han aprendido a hacer un tipo similar de filtrado sobre cosas nuevas de las que escuchan. Ni siquiera comienzan a prestar atención hasta que han oído hablar de algo diez veces. Están perfectamente justificados: la mayoría de las nuevas modas resultan ser una pérdida de tiempo, y eventualmente desaparecen. Al retrasar el aprendizaje de VRML, evité tener que aprenderlo en absoluto.

Así que cualquiera que invente algo nuevo tiene que esperar seguir repitiendo su mensaje durante años antes de que las personas comiencen a entenderlo. Escribimos lo que fue, hasta donde sé, la primera aplicación basada en servidor web, y nos llevó años hacerle entender a la gente que no tenía que ser descargada. No era que fueran estúpidos. Simplemente nos tenían desconectados.

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 nota que estás ahí que prestan atención; es cuando notan que todavía estás ahí.

Es igualmente bueno que generalmente toma un tiempo ganar impulso. La mayoría de las tecnologías evolucionan bastante incluso después de ser lanzadas 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 sacan a la luz 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 nueva tecnología: el método de crecimiento orgánico y el método del gran estallido. El método de crecimiento orgánico es ejemplificado por la clásica startup de garaje subfinanciada y a la suerte. Un par de chicos, trabajando en la oscuridad, desarrollan alguna nueva tecnología. La lanzan sin marketing y inicialmente tienen solo unos pocos (fanáticamente devotos) usuarios. Continúan mejorando la tecnología, y mientras tanto su base de usuarios crece de boca en boca. Antes de que se den cuenta, son grandes.

El otro enfoque, el método del gran estallido, es ejemplificado por la startup respaldada por capital de riesgo, fuertemente comercializada. Se apresuran a desarrollar un producto, lo lanzan con gran publicidad, y de inmediato (esperan) tienen una gran base de usuarios.

Generalmente, los chicos del garaje envidian a los chicos del gran estallido. Los chicos del gran estallido son suaves y seguros 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 chicos del crecimiento orgánico, sentados en su garaje, se sienten pobres y no amados. Y sin embargo, creo que a menudo se equivocan al sentirse mal por sí mismos. El crecimiento orgánico parece producir mejor tecnología y fundadores más ricos que el método del gran estallido. Si miras las tecnologías dominantes hoy en día, encontrarás que la mayoría de ellas crecieron orgánicamente.

Este patrón no solo se aplica a las empresas. También lo ves en la investigación patrocinada. Multics y Common Lisp fueron proyectos de gran estallido, y Unix y MacLisp fueron proyectos de crecimiento orgánico.

10 Rediseño

"La mejor escritura es reescritura", escribió E. B. White. Cada buen escritor sabe esto, y es cierto para el software también. 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 ser capaz de pensar ¿qué tan 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 verdadera contradicción aquí. Quieres 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 tenido hasta ahora.

Las personas que hacen un buen trabajo a menudo piensan que lo que sea en lo que están trabajando no es bueno. 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 la esperanza y la preocupación equilibradas, impulsarán un proyecto hacia adelante de la misma manera que tus dos piernas impulsan una bicicleta hacia adelante. 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 fría luz de la mañana, y ves todos sus defectos muy claramente. Pero mientras tu espíritu crítico no supere tu esperanza, podrás mirar tu sistema, admitidamente incompleto, y pensar, ¿qué tan difícil puede ser llegar al resto?, continuando así el ciclo.

Es complicado mantener equilibradas las dos fuerzas. En los hackers jóvenes, predomina el optimismo. Producen algo, están convencidos de que es genial, y nunca lo mejoran. En los hackers viejos, predomina el escepticismo, y ni siquiera se atreverán a asumir proyectos ambiciosos.

Cualquier cosa que puedas hacer para mantener el ciclo de rediseño en marcha es buena. La prosa puede reescribirse una y otra vez hasta que estés satisfecho con ella. Pero el software, como regla, no se rediseña lo suficiente. La prosa tiene lectores, pero el software tiene usuarios. Si un escritor reescribe un ensayo, las personas que leyeron la versión antigua son poco propensas a quejarse de que sus pensamientos se han visto interrumpidos 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 tus usuarios con cuidado, y sé lento en aumentar su número. Tener usuarios es como la optimización: el curso sabio es retrasarlo. Además, como regla general, en cualquier momento puedes salirte con la tuya cambiando más de lo que piensas. Introducir cambios es como quitar un vendaje: el dolor es un recuerdo casi tan pronto como lo sientes.

Todo el mundo sabe 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 trabajo introducir cambios que nadie quiere molestarse. Cualquier cosa que decida un comité tiende a quedarse así, incluso si a la mayoría de los miembros no les gusta.

Incluso un comité de dos se interpone en el rediseño. Esto sucede particularmente en las interfaces entre piezas de software escritas por dos personas diferentes. Para cambiar la interfaz, ambos tienen que acordar cambiarla al mismo tiempo. Y así, las interfaces tienden a no cambiar en absoluto, lo cual es un problema porque tienden a ser una de las partes más ad hoc de cualquier sistema.

Una 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 sean siempre estratos de abstracción apilados verticalmente. Entonces la interfaz tenderá a ser propiedad de uno de ellos. El nivel inferior de dos niveles será un lenguaje en el que se escribe el superior, en cuyo caso el nivel inferior poseerá la interfaz, o será un esclavo, en cuyo caso la interfaz puede ser dictada por el nivel superior.

11 Lisp

Lo que todo esto implica es 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 podemos haber cometido un error al pensar que a los hackers les desagrada la extrañeza de Lisp. Esta ilusión reconfortante puede haber evitado que viéramos el verdadero problema con Lisp, o al menos con Common Lisp, que es que es malo para hacer lo que los hackers quieren hacer. Un lenguaje de hacker necesita bibliotecas poderosas y algo para hackear. Common Lisp no tiene ninguna. Un lenguaje de hacker es conciso y hackeable. Common Lisp no lo es.

La buena noticia es que no es Lisp el que es malo, 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 algún trabajo importante mejor que otros lenguajes.

La historia ofrece algo de aliento. Con el tiempo, sucesivos nuevos lenguajes de programación han tomado más y más características de Lisp. Ya no queda mucho por copiar antes de que el lenguaje que has creado sea Lisp. El último lenguaje de moda, Python, es un Lisp diluido 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 de IA lento con un montón de paréntesis. La biografía oficial de Fritz Kunze evita cuidadosamente mencionar la palabra L. Pero mi suposición es que no deberíamos tener miedo de llamar al nuevo Lisp Lisp. Lisp todavía tiene mucho respeto latente entre los mejores hackers — aquellos 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 Lisp como algo así como el latín o el griego — un lenguaje que deberías aprender como un ejercicio intelectual, aunque en realidad no lo uses:

Lisp vale la pena aprender 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 nunca realmente usas Lisp mucho.

Si no conociera Lisp, leer esto me haría hacer preguntas. Un lenguaje que me haría un mejor programador, si significa algo en absoluto, significa un lenguaje que sería mejor para programar. Y esa es de hecho la implicación de lo que Eric está diciendo.

Mientras esa idea siga flotando, creo que los hackers serán suficientemente receptivos a un nuevo Lisp, incluso si se llama Lisp. Pero este Lisp debe ser un lenguaje de hacker, como los Lisps 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 lo que respecta a las bibliotecas, creo que hay espacio para superar a lenguajes como Perl y Python en su propio juego. Muchas de las nuevas aplicaciones que necesitarán ser escritas en los próximos años serán aplicaciones basadas en servidor. No hay razón para que un nuevo Lisp no tenga bibliotecas de cadenas tan buenas como Perl, y si este nuevo Lisp también tuviera bibliotecas poderosas para aplicaciones basadas en servidor, podría ser muy popular. Los verdaderos hackers no desairará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 una victoria aún mayor tener soporte del lenguaje central para aplicaciones basadas en 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 servidor también nos dan la respuesta a la pregunta de para qué se usará este nuevo Lisp. No haría daño hacer que Lisp sea mejor como lenguaje de scripting para Unix. (Sería difícil hacerlo peor). 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 proporcionar Lisp junto con un sistema completo para soportar aplicaciones basadas en servidor. Lisp es un ajuste natural para aplicaciones basadas en servidor. Los cierres léxicos proporcionan una forma de obtener el efecto de subrutinas cuando la interfaz de usuario es solo una serie de páginas web. Las expresiones s se mapean bien en html, y las macros son buenas para generarlo. Necesitamos mejores herramientas para escribir aplicaciones basadas en servidor, y necesitamos un nuevo Lisp, y los dos funcionarían muy bien juntos.

12 El Lenguaje de Sueño

Como resumen, intentemos describir el lenguaje de sueño del hacker. El lenguaje de sueño es hermoso, limpio y conciso. Tiene un nivel superior interactivo que arranca rápido. 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 para tu aplicación. Todo lo demás ya ha sido hecho por ti.

La sintaxis del lenguaje es breve hasta el punto de ser defectuosa. Nunca tienes que escribir un carácter innecesario, o incluso usar mucho la tecla de 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 muy buen profiler que te dice dónde enfocar tu atención. Puedes hacer que los bucles internos sean increíblemente rápidos, incluso escribiendo bytecode 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 mirar mucho en el manual. 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 tan cuidadosamente diseñadas como el lenguaje central. Las bibliotecas funcionan bien juntas; todo en el lenguaje encaja como las partes de una buena cámara. Nada está obsoleto, o retenido 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 está construido en capas. Las abstracciones de nivel superior están construidas de una manera muy transparente a partir de abstracciones de nivel inferior, a las que puedes acceder si lo deseas.

Nada está oculto para ti que no tenga que estarlo. 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, incluso su sintaxis, y cualquier cosa que escribas tiene, tanto como sea 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 ambas.

[2] En Cuando el aire golpea tu cerebro, el neurocirujano Frank Vertosick relata una conversación en la que su residente principal, Gary, habla sobre la diferencia entre cirujanos e internistas ("pulgas"):

Gary y yo pedimos una gran pizza y encontramos un booth abierto. El jefe encendió un cigarrillo. "Mira a esas malditas pulgas, parloteando sobre alguna enfermedad que verán una vez en sus vidas. Ese es el problema con las pulgas, solo les gusta lo bizarro. Odian sus casos de pan y mantequilla. Esa es la diferencia entre nosotros y las malditas pulgas. Verás, amamos las grandes hernias discales lumbares jugosas, pero ellos odian la hipertensión...."

Es difícil pensar en una hernia discal lumbar como jugosa (excepto literalmente). Y, sin embargo, creo que sé lo que quieren decir. A menudo he tenido un error jugoso que rastrear. Alguien que no es programador encontraría difícil imaginar que podría haber placer en un error. Seguramente es mejor si todo simplemente funciona. En un sentido, lo es. Y, sin embargo, hay innegablemente una satisfacción sombría en rastrear ciertos tipos de errores.