LA CONCISIÓN ES PODER
OriginalMayo de 2002
"La cantidad de significado comprimido en un espacio pequeño por los signos algebraicos, es otra circunstancia que facilita los razonamientos que estamos acostumbrados a realizar con su ayuda."
- Charles Babbage, citado en la conferencia de Iverson sobre el premio Turing
En la discusión sobre los temas planteados por Revenge of the Nerds en la lista de correo LL1, Paul Prescod escribió algo que se me quedó grabado en la mente.
El objetivo de Python es la regularidad y la legibilidad, no la concisión.
A primera vista, parece una afirmación bastante condenatoria sobre un lenguaje de programación. Hasta donde sé, concisión = potencia. Si es así, entonces, sustituyendo, obtenemos
El objetivo de Python es la regularidad y la legibilidad, no la potencia.
Y esto no parece un compromiso (si es que lo es) que uno quisiera hacer. No es muy diferente decir que el objetivo de Python no es ser eficaz como lenguaje de programación.
¿La concisión es igual a potencia? Me parece una pregunta importante, quizá la más importante para cualquiera que esté interesado en el diseño de lenguajes, y una que sería útil afrontar directamente. No estoy seguro todavía de que la respuesta sea un simple sí, pero parece una buena hipótesis con la que empezar.
Hipótesis
Mi hipótesis es que la concisión es poder, o es lo suficientemente parecida como para que, salvo en ejemplos patológicos, se puedan tratar como idénticos.
Me parece que la concisión es el propósito de los lenguajes de programación. A los ordenadores les encantaría que les dijeran qué hacer directamente en lenguaje de máquina. Creo que la razón principal por la que nos tomamos la molestia de desarrollar lenguajes de alto nivel es para conseguir apalancamiento, de modo que podamos decir (y, lo que es más importante, pensar) en 10 líneas de un lenguaje de alto nivel lo que requeriría 1000 líneas de lenguaje de máquina. En otras palabras, el objetivo principal de los lenguajes de alto nivel es hacer que el código fuente sea más pequeño.
Si el propósito de los lenguajes de alto nivel es un código fuente más pequeño, y el poder de algo es qué tan bien logra su propósito, entonces la medida del poder de un lenguaje de programación es qué tan pequeños hace sus programas.
Por el contrario, un lenguaje que no hace que sus programas sean pequeños está haciendo un mal trabajo de lo que los lenguajes de programación se supone que deben hacer, como un cuchillo que no corta bien o una impresión ilegible.
Métrica
¿Pequeño en qué sentido? La medida más común del tamaño del código son las líneas de código, pero creo que esta métrica es la más común porque es la más fácil de medir. No creo que nadie crea realmente que sea la verdadera prueba de la longitud de un programa. Los distintos lenguajes tienen diferentes convenciones sobre cuánto se debe poner en una línea; en C, muchas líneas no tienen nada más que uno o dos delimitadores.
Otra prueba fácil es el número de caracteres de un programa, pero esto tampoco es muy bueno; algunos lenguajes (Perl, por ejemplo) simplemente usan identificadores más cortos que otros.
Creo que una mejor medida del tamaño de un programa sería la cantidad de elementos, donde un elemento es cualquier cosa que sería un nodo distinto si dibujaras un árbol que representara el código fuente. El nombre de una variable o función es un elemento; un entero o un número de punto flotante es un elemento; un segmento de texto literal es un elemento; un elemento de un patrón o una directiva de formato es un elemento; un nuevo bloque es un elemento. Hay casos límite (¿-5 son dos elementos o uno?) pero creo que la mayoría de ellos son los mismos para todos los lenguajes, por lo que no afectan mucho a las comparaciones.
Esta métrica necesita ser desarrollada y podría requerir interpretación en el caso de lenguajes específicos, pero creo que intenta medir lo correcto, que es el número de partes que tiene un programa. Creo que el árbol que dibujarías en este ejercicio es lo que tienes que hacer en tu cabeza para concebir el programa y, por lo tanto, su tamaño es proporcional a la cantidad de trabajo que tienes que hacer para escribirlo o leerlo.
Diseño
Este tipo de métricas nos permitirían comparar distintos lenguajes, pero ese no es, al menos para mí, su principal valor. El principal valor del test de concisión es el de servir de guía en el diseño de lenguajes. La comparación más útil entre lenguajes es entre dos variantes potenciales del mismo lenguaje. ¿Qué puedo hacer en el lenguaje para que los programas sean más cortos?
Si la carga conceptual de un programa es proporcional a su complejidad y un programador determinado puede tolerar una carga conceptual fija, entonces esto es lo mismo que preguntar: ¿qué puedo hacer para que los programadores puedan lograr el máximo rendimiento? Y eso me parece idéntico a preguntar: ¿cómo puedo diseñar un buen lenguaje?
(Por cierto, nada hace más patente que el viejo dicho de que "todos los lenguajes son equivalentes" es falso que diseñar lenguajes. Cuando estás diseñando un lenguaje nuevo, estás comparando constantemente dos lenguajes (el lenguaje si hiciera x y el lenguaje si no lo hiciera) para decidir cuál es mejor. Si esta fuera realmente una pregunta sin sentido, bien podrías lanzar una moneda.)
Intentar ser breve parece una buena forma de encontrar nuevas ideas. Si puedes hacer algo que acorte muchos programas diferentes, probablemente no sea una coincidencia: probablemente hayas descubierto una nueva abstracción útil. Incluso podrías ser capaz de escribir un programa que te ayude buscando patrones repetidos en el código fuente. Entre otros lenguajes, aquellos que tienen reputación de breves serían los que se deberían considerar para encontrar nuevas ideas: Forth, Joy, Icon.
Comparación
La primera persona que escribió sobre estos temas, hasta donde yo sé, fue Fred Brooks en el Mythical Man Month . Escribió que los programadores parecían generar aproximadamente la misma cantidad de código por día independientemente del lenguaje. Cuando leí esto por primera vez cuando tenía poco más de veinte años, fue una gran sorpresa para mí y parecía tener enormes implicaciones. Significaba que (a) la única manera de escribir software más rápido era usar un lenguaje más conciso y (b) alguien que se tomara la molestia de hacer esto podría dejar atrás a los competidores que no lo hicieran.
La hipótesis de Brooks, si es cierta, parece estar en el corazón mismo del hacking. En los años transcurridos desde entonces, he prestado mucha atención a cualquier evidencia que he podido conseguir sobre la cuestión, desde estudios formales hasta anécdotas sobre proyectos individuales. No he visto nada que la contradiga.
Todavía no he visto evidencia que me parezca concluyente, y no espero verla. Estudios como la comparación de lenguajes de programación de Lutz Prechelt, si bien generan el tipo de resultados que esperaba, tienden a utilizar problemas que son demasiado cortos para ser pruebas significativas. Una mejor prueba de un lenguaje es lo que sucede en programas que tardan un mes en escribirse. Y la única prueba real, si crees como yo que el propósito principal de un lenguaje es que sea bueno para pensar en él (en lugar de simplemente decirle a una computadora qué hacer una vez que lo hayas pensado), es qué cosas nuevas puedes escribir en él. Por lo tanto, cualquier comparación de lenguajes en la que tengas que cumplir con una especificación predefinida está probando algo ligeramente equivocado.
La verdadera prueba de un lenguaje es lo bien que se puede descubrir y resolver nuevos problemas, no lo bien que se puede utilizar para resolver un problema que alguien más ya ha formulado. Se trata de dos criterios muy diferentes. En el arte, los medios como el bordado y el mosaico funcionan bien si se sabe de antemano lo que se quiere hacer, pero son absolutamente pésimos si no se sabe. Cuando se quiere descubrir la imagen a medida que se va haciendo (como ocurre con cualquier cosa tan compleja como la imagen de una persona, por ejemplo), se necesita utilizar un medio más fluido, como el lápiz, la tinta china o la pintura al óleo. Y, de hecho, la forma en que se hacen los tapices y los mosaicos en la práctica es haciendo primero una pintura y luego copiándola (la palabra "caricatura" se utilizó originalmente para describir una pintura destinada a este fin).
Esto significa que es poco probable que tengamos comparaciones precisas del poder relativo de los lenguajes de programación. Tendremos comparaciones precisas, pero no exactas. En particular, los estudios explícitos con el propósito de comparar lenguajes, dado que probablemente utilizarán problemas pequeños y necesariamente utilizarán problemas predefinidos, tenderán a subestimar el poder de los lenguajes más potentes.
Los informes de campo, aunque necesariamente serán menos precisos que los estudios "científicos", probablemente sean más significativos. Por ejemplo, Ulf Wiger de Ericsson realizó un estudio que concluyó que Erlang era entre 4 y 10 veces más conciso que C++ y proporcionalmente más rápido para desarrollar software en:
Las comparaciones entre los proyectos de desarrollo internos de Ericsson indican una productividad similar por línea/hora, incluidas todas las fases de desarrollo de software, independientemente del lenguaje utilizado (Erlang, PLEX, C, C++ o Java). Lo que diferencia a los distintos lenguajes es el volumen del código fuente.
El estudio también aborda explícitamente un aspecto que sólo estaba implícito en el libro de Brooks (ya que él medía líneas de código depurado): los programas escritos en lenguajes más potentes tienden a tener menos errores. Esto se convierte en un fin en sí mismo, posiblemente más importante que la productividad del programador, en aplicaciones como los conmutadores de red.
La prueba del sabor
En definitiva, creo que hay que dejarse llevar por el instinto. ¿Qué se siente al programar en el lenguaje? Creo que la forma de encontrar (o diseñar) el mejor lenguaje es volverse hipersensible a lo bien que un lenguaje te permite pensar, y luego elegir/diseñar el lenguaje que te resulte más cómodo. Si alguna característica del lenguaje te resulta incómoda o restrictiva, no te preocupes, ya lo sabrás.
Esa hipersensibilidad tiene un precio. Descubrirás que no soportas programar en lenguajes complicados. Me parece insoportablemente restrictivo programar en lenguajes sin macros, de la misma manera que alguien acostumbrado a la tipificación dinámica encuentra insoportablemente restrictivo tener que volver a programar en un lenguaje en el que tienes que declarar el tipo de cada variable y no puedes hacer una lista de objetos de diferentes tipos.
No soy el único. Conozco a muchos hackers de Lisp a los que les ha pasado esto. De hecho, la medida más precisa del poder relativo de los lenguajes de programación podría ser el porcentaje de personas que conocen el lenguaje y que aceptarían cualquier trabajo en el que pudieran utilizarlo, independientemente del dominio de aplicación.
Restricción
Creo que la mayoría de los hackers saben lo que significa que un lenguaje te resulte restrictivo. ¿Qué ocurre cuando sientes eso? Creo que es la misma sensación que tienes cuando la calle que quieres tomar está bloqueada y tienes que dar un largo rodeo para llegar a donde querías ir. Hay algo que quieres decir y el lenguaje no te lo permite.
Lo que realmente ocurre aquí, creo, es que un lenguaje restrictivo es aquel que no es lo suficientemente conciso. El problema no es simplemente que no puedas decir lo que planeaste decir, sino que el desvío que el lenguaje te obliga a tomar es más largo. Prueba este experimento mental. Supón que hay un programa que quieres escribir y el lenguaje no te permite expresarlo de la manera que planeaste, sino que te obliga a escribir el programa de alguna otra manera que sea más corta. Para mí, al menos, eso no sería muy restrictivo. Sería como si la calle que quieres tomar estuviera bloqueada y el policía en la intersección te indicara un atajo en lugar de un desvío. ¡Genial!
Creo que la mayor parte (¿el noventa por ciento?) de la sensación de restricción proviene de verse obligado a hacer que el programa que escribes en el lenguaje sea más largo que el que tienes en tu cabeza. La restricción es, en su mayoría, falta de concisión. Por lo tanto, cuando un lenguaje parece restrictivo, lo que eso significa (en su mayoría) es que no es lo suficientemente conciso, y cuando un lenguaje no es conciso, parecerá restrictivo.
Legibilidad
La cita con la que empecé menciona otras dos cualidades: regularidad y legibilidad. No estoy seguro de qué es la regularidad ni de qué ventaja, si es que tiene alguna, tiene el código regular y legible sobre el código que es simplemente legible. Pero creo que sé lo que se entiende por legibilidad y creo que también está relacionada con la concisión.
Aquí debemos tener cuidado de distinguir entre la legibilidad de una línea de código individual y la legibilidad de todo el programa. Lo que importa es lo segundo. Estoy de acuerdo en que es probable que una línea de Basic sea más legible que una línea de Lisp. Pero un programa escrito en Basic tendrá más líneas que el mismo programa escrito en Lisp (especialmente una vez que se pasa a Greenspunland). El esfuerzo total de leer el programa en Basic seguramente será mayor.
esfuerzo total = esfuerzo por línea x número de líneas
No estoy tan seguro de que la legibilidad sea directamente proporcional a la concisión como de que lo sea la potencia, pero sin duda la concisión es un factor (en el sentido matemático; véase la ecuación anterior) de la legibilidad. Por lo tanto, puede que ni siquiera tenga sentido decir que el objetivo de un idioma es la legibilidad, no la concisión; podría ser como decir que el objetivo era la legibilidad, no la legibilidad.
Lo que significa la legibilidad por línea para el usuario que se encuentra con el lenguaje por primera vez es que el código fuente no parecerá amenazador . Por lo tanto, la legibilidad por línea podría ser una buena decisión de marketing, incluso si es una mala decisión de diseño. Es isomorfa a la técnica muy exitosa de permitir que la gente pague en cuotas: en lugar de asustarlos con un alto precio inicial, les dices el bajo pago mensual. Sin embargo, los planes de pago en cuotas son una pérdida neta para el comprador, como la mera legibilidad por línea probablemente lo sea para el programador. El comprador va a realizar muchos de esos pagos muy, muy bajos; y el programador va a leer muchas de esas líneas legibles individualmente.
Esta disyuntiva es anterior a los lenguajes de programación. Si estás acostumbrado a leer novelas y artículos de periódico, tu primera experiencia de lectura de un artículo de matemáticas puede ser desalentadora. Puede llevarte media hora leer una sola página. Y, sin embargo, estoy bastante seguro de que la notación no es el problema, aunque pueda parecerlo. El artículo de matemáticas es difícil de leer porque las ideas son difíciles. Si expresaras las mismas ideas en prosa (como tenían que hacer los matemáticos antes de que desarrollaran notaciones concisas), no serían más fáciles de leer, porque el artículo crecería hasta el tamaño de un libro.
¿Hasta qué punto?
Muchas personas han rechazado la idea de que la concisión equivale a potencia. Creo que sería más útil, en lugar de simplemente argumentar que son lo mismo o no, preguntar: ¿hasta qué punto la concisión equivale a potencia? Porque claramente la concisión es una parte importante de la función de los lenguajes de alto nivel. Si no es solo para eso, entonces ¿para qué más sirven y qué importancia, relativamente, tienen esas otras funciones?
No propongo esto sólo para que el debate sea más civilizado. Realmente quiero saber la respuesta. ¿Cuándo, si es que alguna vez, un lenguaje es demasiado sucinto para su propio bien?
La hipótesis con la que empecé fue que, salvo en ejemplos patológicos, pensaba que la concisión podía considerarse idéntica a la potencia. Lo que quería decir era que, en cualquier lenguaje que alguien diseñara, serían idénticos, pero que si alguien quisiera diseñar un lenguaje explícitamente para refutar esta hipótesis, probablemente podría hacerlo. En realidad, ni siquiera estoy seguro de eso.
Idiomas, no programas
Debemos tener claro que estamos hablando de la concisión de los lenguajes, no de los programas individuales. Sin duda, es posible que los programas individuales estén escritos de forma demasiado densa.
Escribí sobre esto en On Lisp . Una macro compleja puede tener que ahorrar muchas veces su propia longitud para justificarse. Si escribir una macro complicada puede ahorrarle diez líneas de código cada vez que la usa, y la macro en sí misma es diez líneas de código, entonces obtendrá un ahorro neto en líneas si la usa más de una vez. Pero eso aún podría ser una mala decisión, porque las definiciones de macro son más difíciles de leer que el código ordinario. Es posible que tenga que usar la macro diez o veinte veces antes de que produzca una mejora neta en la legibilidad.
Estoy seguro de que todos los lenguajes tienen sus pros y sus contras (aunque sospecho que los riesgos aumentan a medida que el lenguaje se vuelve más potente). Todos los programadores deben haber visto código que alguna persona inteligente ha acortado ligeramente mediante el uso de trucos de programación dudosos.
Así que no hay discusión al respecto, al menos no por mi parte. Los programas individuales pueden ser demasiado concisos para su propio bien. La pregunta es: ¿puede un lenguaje serlo? ¿Puede un lenguaje obligar a los programadores a escribir código que sea corto (en elementos) a expensas de la legibilidad general?
Una de las razones por las que es difícil imaginar que un lenguaje sea demasiado conciso es que si hubiera una forma excesivamente compacta de expresar algo, probablemente también habría una forma más larga. Por ejemplo, si crees que los programas Lisp que utilizan muchas macros o funciones de orden superior son demasiado densos, podrías, si lo prefieres, escribir código que sea isomorfo a Pascal. Si no quieres expresar factorial en Arc como una llamada a una función de orden superior
(rec zero 1 * 1-)
También puedes escribir una definición recursiva:
(rfn fact (x) (if (zero x) 1 (* x (fact (1- x)))))
Aunque no se me ocurre ningún ejemplo en este momento, me interesa saber si un lenguaje puede ser demasiado conciso. ¿Existen lenguajes que te obliguen a escribir código de una manera confusa e incomprensible? Si alguien tiene ejemplos, me interesaría mucho verlos.
(Recordatorio: lo que estoy buscando son programas que sean muy densos según la métrica de "elementos" esbozada anteriormente, no simplemente programas que sean cortos porque se pueden omitir los delimitadores y todo tiene un nombre de un solo carácter).