CONCISIÓN ES PODER


Mayo 2002
 
"La cantidad de significado comprimido en un pequeño espacio de signos algebraicos, es otra circunstancia que facilita los razonamientos que estamos acostumbrados a llevar a cabo con su ayuda."

- Charles Babbage, citado por Iverson en su discurso del Premio Turing



En la discusión sobre las cuestiones planteadas por La Venganza de los Nerds en la lista de correo LL1, Paul Prescod escribió algo que se quedó en mi mente.

El objetivo de Python es la regularidad y la facilidad de lectura, no la concisión.

Si lo consideramos, afirmar esto acerca de un lenguaje de programación parece algo bastante desafortunado. Por lo que puedo decir, concisión = poder. Si es así, sustituyendo, tenemos

El objetivo de Python es la regularidad y la facilidad de lectura, no la potencia.

y esta no parece una concesión (si es una concesión) que te gustaría hacer. No esta lejos de decir que el objetivo de Python no es ser eficaz como lenguaje de programación.

¿Es concisión = poder? Esta me parece una pregunta importante, tal vez la más importante para cualquier persona interesada en diseño de lenguajes, y una que sería útil confrontar directamente. No estoy seguro ahora de que la respuesta sea un simple sí, pero para empezar me parece una buena hipótesis.
 

Hipótesis


Mi hipótesis es que la concisión equivale a poder, o está lo suficientemente cerca que, salvo en ejemplos patológicos, se les puede tratar como iguales.

Me parece que la concisión es para lo que son los lenguajes de programación. Las computadoras serían igual de felices si se les dijera 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 obtener una ventaja, de manera que podamos decir (y lo más importante, pensar) en 10 líneas de lenguaje de alto nivel, lo que requeriría 1000 líneas de lenguaje de máquina. En otras palabras, la principal idea de los lenguajes de alto nivel es hacer que el código fuente sea más pequeño.

Si el proposito de los lenguajes de alto nivel es un código fuente más pequeño, y el poder de algo es que tan bien logra su propósito, entonces la medida de la potencia de un lenguaje de programación es que tan pequeños hace tus programas.

Por el contrario, un lenguaje que no hace tus programas pequeños está haciendo un mal trabajo de lo que se supone tienen que hacer los lenguajes de programación, como un cuchillo que no corta bien, o una impresión ilegible.
 

Métrica


Sin embargo ¿pequeños en qué sentido? La medida más común del tamaño del código es líneas de código. Pero creo que esta medida es la más común porque es la más fácil de medir. No creo que nadie realmente crea que es la verdadera prueba de la longitud de un programa. Lenguajes diferentes tienen diferentes convenciones para que tanto se debe poner en una línea, en C una gran cantidad de líneas no tienen nada en ellas, aparte de uno o dos delimitadores.

Otra prueba sencilla es el número de caracteres en un programa, pero esta tampoco es muy buena, y algunos lenguajes (Perl, por ejemplo), sólo utilizan identificadores más cortos que otros.

Creo que una mejor medida del tamaño de un programa sería el número de elementos, donde un elemento es algo que pueda ser un nodo inconfundible si se dibujara un árbol que representa 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 extremos (¿es -5 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 refinarse, y podría requerir interpretación en el caso de lenguajes específicos, pero creo que intenta medir las cosas adecuadas, que son el número de partes que tiene un programa. Creo que el árbol que dibujarás con este ejercicio es lo que tienes que hacer en tu cabeza con el fin de concebir el programa, por lo que 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 permitira comparar diferentes lenguajes, pero ese no es, al menos para mí, su principal valor. El principal valor de la prueba de concisión es como una guía para el diseño de lenguajes. La comparación más útil entre lenguajes se da entre dos posibles variantes del mismo lenguaje. ¿Qué puedo hacer en el lenguaje para hacer programas más cortos?

Si la carga conceptual de un programa es proporcional a su complejidad, y un programador dado puede tolerar una carga conceptual fija, entonces esto es igual a preguntar, ¿qué puedo hacer para permitir a los programadores conseguir hacer más? Y eso me parece idéntico a preguntar: ¿Cómo puedo diseñar un buen lenguaje?

(Por cierto, nada hace más claramente evidente que el viejo dicho "todos los lenguajes son equivalentes" es falso, que el diseñar lenguajes. Cuando estás diseñando un nuevo lenguaje, estás constantemente comparando dos lenguajes —el lenguaje si hiciera x, y si no lo hiciera— para decidir cuál es mejor. Si esta fuera realmente una pregunta irrelevante, bien podrías decidirlo lanzando una moneda.)

Buscar la concisión parece una buena manera de encontrar nuevas ideas. Si puedes hacer algo que haga más cortos a muchos programas diferentes, probablemente no sea una casualidad: es probable que hayas descubierto una abstracción nueva y útil. Podrías incluso ser capaz de escribir un programa para ayudar a buscar el código fuente de patrones que se repiten. Entre otros lenguajes, los que tienen una reputación de concisión serán a los que habrá que recurrir en busca de nuevas ideas: Forth, Joy, Icon.
 

Comparación


La primera persona en escribir sobre estos temas, que yo sepa, fue Fred Brooks en The Mythical Man Month. [a] Ahi mencionó que los programadores parecen generar la misma cantidad de código por día, independientemente del lenguaje. Cuando leí por primera vez esto a la edad de veinte años, fue una gran sorpresa para mí y parecía tener enormes implicaciones. Quería decir que (a) la única forma de conseguir escribir software más rápido era utilizar un lenguaje más conciso, y (b) alguien que se tomara la molestia de hacer esto dejaría a sus competidores que no lo hicieran mordiendo el polvo.

La hipótesis de Brooks, si es verdadera, parece estar en el corazón de lo que significa hackear. En los años posteriores, he prestado mucha atención a cualquier evidencia que pudiera conseguir sobre esta cuestión, desde estudios formales hasta anécdotas acerca de proyectos individuales. No he visto nada que la contradiga.

Todavía no he visto evidencia que me parezca concluyente, y no lo espero. Estudios comparativos de lenguajes de programación como el de Lutz Prechelt, mientras que generan el mismo tipo de resultados que esperaría, tienden a utilizar problemas demasiado pequeños para considerarlos pruebas significativas. Una mejor prueba de un lenguaje es lo que sucede en programas que tardan un mes para escribirse. Y la única prueba real, si como yo crees que el principal propósito de un lenguaje es que debe ser bueno como herramienta para ayudarte a pensar (y no sólo para decirle a una computadora qué hacer una vez que hayas pensado en ello) es qué cosas nuevas puedes escribir en él. Por lo que cualquier comparación de lenguajes en el que tienes que cumplir con una especificación predefinida está ligeramente poniendo a prueba la cosa equivocada.

La verdadera prueba de un lenguaje es que tan bien puedes descubrir y resolver nuevos problemas, no lo bien que se pueda utilizar para resolver un problema que ya alguien ha formulado. Estos dos criterios son muy diferentes. En el arte, medios como el bordado y el mosaico funcionan bien si sabes de antemano lo que quieres hacer, pero son terriblemente malos si no. Cuando quieres descubrir la imagen mientras la haces —como tienes que hacerlo con algo tan complejo como la imagen de una persona, por ejemplo— es necesario utilizar un medio más fluido como el lápiz o la tinta o la pintura de aceite. Y, de hecho, en la práctica la forma en que los tapices y mosaicos se hacen es creando una pintura primero, y luego copiarla. (La palabra "cartoon" fue originalmente utilizada para describir un cuadro destinado a este fin).

Lo que esto significa es que probablemente nunca tendremos comparaciones correctas del poder relativo de los lenguajes de programación. Tendremos comparaciones precisas, pero no correctas. En particular, los estudios explícitos cuyo propósito sea comparar los lenguajes, tenderán a subestimar el poder de los lenguajes más potentes ya que probablemente se utilizarán problemas pequeños, y estos necesariamente serán predefinidos.

Los informes desde el terreno, a pesar de que necesariamente serán menos precisos que los estudios "científicos", probablemente serán más significativos. Por ejemplo, Ulf Wiger de Ericsson realizó un estudio que concluyó que Erlang [b] era de 4 a 10 veces más conciso que C++, y proporcionalmente más rápido para el desarrollo de software:
 
Las comparaciones entre los proyectos internos de desarrollo de Ericsson indican línea/hora de productividad similares, incluyendo todas las fases de desarrollo de software, independientemente del lenguaje (Erlang, PLEX, C, C++, o Java) utilizado. Lo que diferencia a los diferentes lenguajes es entonces el volumen del código fuente.

El estudio también se ocupa explícitamente de un punto que sólo estaba implícito en el libro de Brooks (ya que él midió 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 tales como conmutadores de red.
 

La Prueba del Gusto

 
En última instancia, creo que tienes que seguir tus instintos. ¿Cómo se siente programar en el lenguaje? Creo que la manera de encontrar (o diseñar) el mejor lenguaje es ser extremadamente sensibles a la eficacia con que un lenguaje te permite pensar, a continuación, elegir/diseñar el lenguaje que se siente mejor. Si alguna característica del lenguaje es incomoda o restrictiva, no te preocupes, lo sabrás.

Esta hipersensibilidad tendrá un costo. Descubrirás que no soportaras programar en lenguajes toscos. Me resulta insoportablemente restrictivo programar en lenguajes sin macros, al igual que a alguien que utiliza el tipado dinámico le resulta insoportablemente restrictivo tener que volver a programar en un lenguaje donde tienes que declarar el tipo de cada variable, y no puedes hacer una lista de objetos de diferentes tipos.

No soy el único. Conozco muchos hackers de Lisp a los que esto les ha sucedido. De hecho, la medida más precisa de la potencia relativa de los lenguajes de programación podría ser que porcentaje de personas que conocen el lenguaje aceptarían un empleo en el que puedan utilizarlo, independientemente del dominio de la aplicación.
 

Restricción


Creo que la mayoría de los hackers saben lo que significa un lenguaje que se siente restrictivo. ¿Qué esta sucediendo cuando sientes eso? Creo que es la misma sensación que tienes cuando la calle que deseas tomar está bloqueada, y hay 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 está sucediendo aquí, creo yo, es que un lenguaje restrictivo es aquel que no es lo suficientemente conciso. El problema no es simplemente que no puedes decir lo que pensabas. Es que la desviación que el lenguaje hace que tomes es más larga. Prueba este experimento mental. Supongamos que hay algún programa que quisieras escribir, y el lenguaje no te permite expresarlo de la manera en que pensabas, sino que te obliga a escribir el programa de alguna otra forma más corta. Al menos para mí, eso no se sentiría muy restrictivo. Sería como si la calle que querías tomar esta bloqueada, y el policía en el cruce te dirige a un atajo en lugar de un rodeo. ¡Genial!

Creo que la mayoría (¿el noventa por ciento?) de la sensación de restricción proviene de estar obligados a hacer que el programa que escribes en el lenguaje sea más largo que el que tienes en tu cabeza. Restricción es sobre todo falta de concisión. Así que cuando un lenguaje se siente restrictivo, lo que (en su mayoría) quiere decir es que no es lo suficientemente conciso, y cuando un lenguaje no es conciso, se siente restrictivo.
 

Legibilidad


La cita con la que comencé menciona otras dos cualidades: regularidad y legibilidad. No estoy seguro de lo que la regularidad es, o qué ventaja tenga, si existe, el código que es regular y fácil de leer sobre el código que es meramente legible. Pero creo que sé lo que significa la legibilidad, y creo que también está relacionado con la concisión.

Tenemos que ser cuidadosos aquí para distinguir entre la legibilidad de una línea de código individual y la legibilidad de todo el programa. Es la segunda la que importa. Estoy de acuerdo en que una línea de Basic probablemente sea más legible que una línea de Lisp. Sin embargo, un programa escrito en Basic tendrá más líneas que el mismo programa escrito en Lisp (en especial una vez que entres en Greenspunland) [c]. El esfuerzo total de la lectura del programa 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 lo estoy acerca de que el poder lo es, pero ciertamente la concisión es un factor (en el sentido matemático; véase la ecuación anterior) en la legibilidad. Por lo que tal vez ni siquiera importe decir que el objetivo de un lenguaje es la legibilidad, no la concisión; sería como decir que el objetivo era mejorar la legibilidad, no la legibilidad.

Lo que legibilidad-por-línea quiere decir, al usuario que se encuentra con el lenguaje por primera vez, es que el código fuente lucirá inofensivo. Así, la legibilidad-por-línea podría ser una buena decisión mercadológica, incluso si es una mala decisión de diseño. Es isomorfo a la exitosa técnica de permitir que la gente pague en abonos: en vez de asustarlos con un precio inicial alto, les dices el bajo pago mensual. Sin embargo, los planes de pago son una perdida neta para el comprador, como la mera lectura por línea, probablemente lo es para el programador. El comprador hará muchos de esos pagos bajos, bajos; y el programador leerá muchas de esas legibles líneas individuales.

Esta concesión es anterior a los lenguajes de programación. Si estás acostumbrado a leer novelas y artículos de prensa, tu primera experiencia al leer un documento de matemáticas puede ser muy mala. Podría tomar media hora leer una sola página. Y, sin embargo, estoy bastante seguro de que la notación no es el problema, aunque pueda sentirse así. 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 hacerlo los matemáticos antes de que se desarrollaran las 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?


Un número de personas han rechazado la idea de que concisión = poder. Creo que sería más útil, en lugar de simplemente argumentar que lo son o no, preguntar: ¿hasta qué punto concisión = poder? Porque evidentemente la concisión es una gran parte de lo que son los lenguajes de alto nivel. Si no es para lo que son, entonces ¿para qué otra cosa sirven, y que tan importantes, relativamente, son estas otras funciones?

No estoy proponiendo esto para hacer el debate más civilizado. Realmente quiero saber la respuesta. ¿Cuándo, si acaso, es un lenguaje demasiado conciso para su propio bien?

La hipótesis con la que inicié era qué, salvo en los ejemplos patológicos, la concisión podía ser considerada idéntica con el poder. Lo que quise decir es que en cualquier lenguaje que alguien diseñe, serían idénticas, pero que si alguien quisiera diseñar un lenguaje explícitamente para refutar esta hipótesis, probablemente podría hacerlo. Ni siquiera estoy seguro de eso, en realidad.
 

Lenguajes, no Programas


Debemos tener claro que estamos hablando de la concisión de los lenguajes, no de los programas individuales. Ciertamente es posible que los programas individuales sean escritos muy densamente.

Escribí sobre esto en On Lisp. Puede ser que una macro compleja deba ahorrar varias veces su propia longitud para justificarse. Si escribir una macro difícil puede ahorrarte diez líneas de código cada vez que la usas, y la macro es en sí misma diez líneas de código, entonces obtienes un ahorro neto en líneas si la utilizas más de una vez. Pero todavía podría ser una mala jugada, porque las definiciones macro son más difíciles de leer que el código común. Tendrías que utilizar la macro diez o veinte veces antes de que produjera una mejora neta en legibilidad.

Estoy seguro de que cada lenguaje tiene tales ventajas y desventajas (aunque sospecho que las probabilidades aumentan, conforme el lenguaje se vuelve más potente). Cada programador debe haber visto el código que alguna persona inteligente ha hecho ligeramente más corto utilizando dudosos trucos de programación.

Así que no hay discusión sobre eso— por lo menos, no de mí. Los programas individuales sin duda pueden ser demasiado concisos para su propio bien. La pregunta es, ¿puede serlo un lenguaje? Puede un lenguaje obligar a los programadores a escribir código que es corto (en elementos) a expensas de la legibilidad en general?

Una de las razones por la que es difícil imaginar que un lenguaje sea demasiado conciso, es que si hay alguna manera excesivamente compacta de decir algo, probablemente también haya una más larga. Por ejemplo, si sientes que los programas Lisp con una gran cantidad de macros o funciones de orden superior son demasiado densas, podrías, si prefieres, escribir código que sea isomorfo a Pascal. Si no deseas 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 ocurren ejemplos, estoy interesado en la cuestión de si un lenguaje puede ser demasiado conciso. ¿Hay lenguajes que te obligan a escribir código de una manera indescifrable e incomprensible? Si alguien tiene ejemplos, estaría muy interesado en verlos.

(Nota: Lo que estoy buscando son programas que son muy densos de acuerdo con la métrica de "elementos" esbozada más arriba, no sólo programas que son cortos porque los delimitadores se pueden omitir y todo tiene un nombre de un solo carácter.)






Traducido del original Succinctness is Power por Paul Graham. Traducción: Armando Alvarez


 

Notas del Traductor


[a] El Mítico Hombre-Mes: Ensayos de ingeniería de Software (en inglés The Mythical Man-Month: Essays on Software Engineering) es un libro de administración de proyectos de Software de Fred Brooks, cuyo tema central está en que "agregar recursos humanos a un proyecto retrasado lo hace demorarse aún más". Esta idea es conocida como la "Ley de Brooks".

El trabajo fue publicado en 1975, y republicado como una versión aniversario en 1995 (ISBN 0-201-83595-9) junto al ensayo "Sin balas de plata" y comentarios por el autor.

Las observaciones de Brooks están basadas en sus experiencias en IBM mientras administraba el desarrollo de OS/360. Para acelerar el desarrollo, se trató infructuosamente de agregar más trabajadores al proyecto que ya estaba retrasado. También apostó que escribir un compilador en ALGOL requería "6 meses de mano de obra" adicional. La tendencia de quienes administran de repetir estos errores llevaron a Brook a la conclusión de "la biblia de la ingeniería de software" porque "todos la leen pero nadie la practica". [Fuente: Wikipedia. Ver]

[b] Erlang es un lenguaje de programación concurrente de propósito general, con recolección de basura y sistema de ejecución. El subconjunto secuencial de Erlang es un lenguaje funcional, con evaluación estricta, asignación única, y tipado dinámico. Para la concurrencia sigue el modelo Actor. Fue diseñado por Ericsson para soportar aplicaciones distribuidas, tolerantes a fallos, soft-real-time, y de funcionamiento ininterrumpido. Es compatible con la sustitución en caliente (hot swap), para que el código pueda cambiarse sin detener el sistema.

Mientras que los hilos se consideran un tema complicado y propenso a errores en la mayoría de los lenguajes, Erlang proporciona características a nivel de lenguaje para la creación y gestión de procesos con el objetivo de simplificar la programación concurrente. A pesar de que toda concurrencia es explícita en Erlang, los procesos se comunican utilizando pase de mensajes (message passing) en lugar de variables compartidas, lo que elimina la necesidad de los cierres de exclusión mutua (locks).
La primera versión fue desarrollada por Joe Armstrong en 1986. Originalmente era un lenguaje propietario dentro de Ericsson, pero fue liberado como código abierto en 1998. [Fuente Wikipedia en Inglés. Ver.]

[c] Aqui hace una referencia a la Regla Décima de Greenspun la cual establece que:
 
Cualquier programa de C o Fortran lo suficientemente complicado contiene una implementación ad hoc especificada de manera informal, lenta, llena de errores, de la mitad de Common Lisp.
 
Esto expresa la opinión de que la flexibilidad y extensibilidad percibida diseñada en el lenguaje de programación Lisp incluye todas las funciones que en teoría son necesarias para escribir un programa informático complejo, y que las implementaciones de base de otros lenguajes de programación a menudo no proporcionan la funcionalidad crítica necesaria para desarrollar programas complejos.