Loading...

LA CONCISIÓN ES PODER

Original

May 2002

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

  • Charles Babbage, citado en la conferencia del Premio Turing de Iverson

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 quedó en mi mente.

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

A primera vista, esto parece una afirmación bastante condenatoria sobre un lenguaje de programación. Hasta donde puedo decir, la concisión = poder. Si es así, entonces sustituyendo, obtenemos

El objetivo de Python es la regularidad y la legibilidad, no el poder.

y esto no parece un compromiso (si es un compromiso) que querrías hacer. No está lejos de decir que el objetivo de Python no es ser efectivo como un lenguaje de programación.

¿La concisión = poder? Esta me parece una pregunta importante, quizás la más importante para cualquiera interesado en el diseño de lenguajes, y una que sería útil confrontar directamente. No me siento seguro aún de que la respuesta sea un simple sí, pero parece una buena hipótesis para comenzar.

Hipótesis

Mi hipótesis es que la concisión es poder, o es lo suficientemente cercana que excepto en ejemplos patológicos puedes tratarlas como idénticas.

Me parece que la concisión es para lo que están los lenguajes de programación. Las computadoras estarían igual de felices de que se les dijera qué hacer directamente en lenguaje de máquina. Creo que la principal razón por la que nos tomamos la molestia de desarrollar lenguajes de alto nivel es para obtener apalancamiento, de modo que podamos decir (y 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 punto principal de los lenguajes de alto nivel es hacer que el código fuente sea más pequeño.

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

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

Métricas

¿Pequeño en qué sentido, sin embargo? La medida más común del tamaño del código es 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 realmente crea que es la verdadera prueba de la longitud de un programa. Diferentes lenguajes tienen diferentes convenciones sobre cuánto deberías poner en una línea; en C, muchas líneas no tienen nada más que un delimitador o dos.

Otra prueba fácil es el número de caracteres en 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 el número de elementos, donde un elemento es cualquier cosa que sería un nodo distinto si dibujaras un árbol representando el código fuente. El nombre de una variable o función es un elemento; un número 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 (¿es -5 dos elementos o uno?) pero creo que la mayoría de ellos son los mismos para cada lenguaje, por lo que no afectan mucho 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étrica nos permitiría comparar diferentes lenguajes, pero eso no es, al menos para mí, su principal valor. El principal valor de la prueba de concisión es como una 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 hacer que los programas sean 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 lo mismo que preguntar, ¿qué puedo hacer para permitir que los programadores hagan más? Y eso me parece idéntico a preguntar, ¿cómo puedo diseñar un buen lenguaje?

(Incidentalmente, nada hace que sea más patentemente obvio que el viejo refrán "todos los lenguajes son equivalentes" es falso que 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 realmente fuera una pregunta sin sentido, podrías simplemente lanzar una moneda).

Aspirar a la concisión parece una buena manera de encontrar nuevas ideas. Si puedes hacer algo que haga muchos programas diferentes más cortos, probablemente no sea una coincidencia: probablemente has descubierto una nueva abstracción útil. Incluso podrías ser capaz de escribir un programa para ayudar buscando código fuente para patrones repetidos. Entre otros lenguajes, aquellos con reputación de concisión serían los que buscar para nuevas ideas: Forth, Joy, Icon.

Comparación

La primera persona en escribir sobre estos temas, hasta donde 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 en mis veintes, fue una gran sorpresa para mí y parecía tener enormes implicaciones. Significaba que (a) la única forma de hacer que el software se escribiera 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 a los competidores que no lo hicieran en el polvo.

La hipótesis de Brooks, si es cierta, parece estar en el mismo corazón de la programación. En los años desde entonces, he prestado mucha atención a cualquier evidencia que pudiera obtener sobre la cuestión, desde estudios formales hasta anécdotas sobre proyectos individuales. No he visto nada que lo contradiga.

Aún no he visto evidencia que me parezca concluyente, y no espero hacerlo. Estudios como la comparación de lenguajes de programación de Lutz Prechelt, aunque generan el tipo de resultados que esperaba, tienden a usar 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 principal propósito de un lenguaje es ser bueno para pensar (en lugar de simplemente decirle a una computadora qué hacer una vez que lo has pensado) es qué cosas nuevas puedes escribir en él. Así que cualquier comparación de lenguajes donde tienes que cumplir con una especificación predefinida está probando ligeramente lo incorrecto.

La verdadera prueba de un lenguaje es cuán bien puedes descubrir y resolver nuevos problemas, no cuán bien puedes usarlo para resolver un problema que alguien más ya ha formulado. Estos dos son criterios bastante diferentes. En el arte, medios como el bordado y el mosaico funcionan bien si sabes de antemano lo que quieres hacer, pero son absolutamente malos si no lo sabes. Cuando quieres descubrir la imagen a medida que la haces-- como tienes que hacer con algo tan complejo como una imagen de una persona, por ejemplo-- necesitas usar un medio más fluido como lápiz o tinta o pintura al óleo. Y de hecho, la forma en que se hacen las tapicerías y los mosaicos en práctica es hacer una pintura primero, y luego copiarla. (La palabra "cartón" se usó originalmente para describir una pintura destinada a este propósito).

Lo que esto significa es 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, porque probablemente usarán problemas pequeños, y necesariamente usarán problemas predefinidos, tenderán a subestimar el poder de los lenguajes más potentes.

Los informes del campo, aunque necesariamente serán menos precisos que los estudios "científicos", son probablemente más significativos. Por ejemplo, Ulf Wiger de Ericsson realizó un estudio que concluyó que Erlang era de 4 a 10 veces más conciso que C++, y proporcionalmente más rápido para desarrollar software en:

Las comparaciones entre proyectos de desarrollo internos de Ericsson indican una productividad similar en líneas/hora, incluyendo todas las fases del desarrollo de software, independientemente de qué lenguaje (Erlang, PLEX, C, C++ o Java) se utilizara. Lo que diferencia a los diferentes lenguajes entonces se convierte en el volumen de código fuente.

El estudio también aborda explícitamente un punto que fue solo 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. Eso 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 de Sabor

En última instancia, creo que tienes que seguir tu instinto. ¿Cómo se siente programar en el lenguaje? Creo que la forma de encontrar (o diseñar) el mejor lenguaje es volverse hipersensible a cuán bien un lenguaje te permite pensar, y luego elegir/diseñar el lenguaje que se siente mejor. Si alguna característica del lenguaje es incómoda o restrictiva, no te preocupes, lo sabrás.

Tal hipersensibilidad tendrá un costo. Descubrirás que no puedes soportar programar en lenguajes torpes. Encuentro que es insoportablemente restrictivo programar en lenguajes sin macros, así como alguien acostumbrado a la tipificación dinámica encuentra 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 a muchos hackers de Lisp a quienes esto les ha pasado. 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 que aceptarán cualquier trabajo donde puedan usar ese lenguaje, independientemente del dominio de la aplicación.

Restrictividad

Creo que la mayoría de los hackers saben lo que significa que un lenguaje se sienta restrictivo. ¿Qué está sucediendo cuando sientes eso? Creo que es la misma sensación que tienes cuando la calle que quieres tomar está bloqueada, y tienes que tomar un largo desvío 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, es que un lenguaje restrictivo es uno que no es lo suficientemente conciso. El problema no es simplemente que no puedes decir lo que planeabas. Es que el desvío que el lenguaje te hace tomar es más largo. Intenta este experimento mental. Supón que hay algún programa que quieres escribir, y el lenguaje no te permite expresarlo de la manera que planeabas, sino que en su lugar te obliga a escribir el programa de alguna otra manera que es más corta. Para mí al menos, eso no se sentiría muy restrictivo. Sería como si la calle que querías tomar estuviera bloqueada, y el policía en la intersección te dirigiera a un atajo en lugar de un desvío. ¡Genial!

Creo que la mayoría (¿noventa por ciento?) de la sensación de restrictividad proviene de ser forzado a hacer que el programa que escribes en el lenguaje sea más largo que uno que tienes en tu cabeza. La restrictividad es principalmente falta de concisión. Así que cuando un lenguaje se siente restrictivo, lo que eso (principalmente) significa es que no es lo suficientemente conciso, y cuando un lenguaje no es conciso, se sentirá restrictivo.

Legibilidad

La cita con la que comencé menciona dos cualidades más, regularidad y legibilidad. No estoy seguro de qué es la regularidad, o qué ventaja, si es que hay alguna, tiene el código que es regular y legible sobre el código que es meramente legible. Pero creo que sé lo que se entiende por legibilidad, y creo que también está relacionado con la concisión.

Debemos tener cuidado aquí de 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 es probablemente más legible que una línea de Lisp. Pero un programa escrito en Basic va a tener más líneas que el mismo programa escrito en Lisp (especialmente una vez que cruces 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 lo estoy de que el poder lo es, pero ciertamente la concisión es un factor (en el sentido matemático; ver la ecuación anterior) en la legibilidad. Así que puede que ni siquiera tenga sentido decir que el objetivo de un lenguaje 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 parecerá inofensivo. Así que 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 isomórfico a la técnica muy exitosa de permitir a las personas pagar en cuotas: en lugar de asustarlas con un alto precio inicial, les dices el bajo pago mensual. Los planes de cuotas son una pérdida neta para el comprador, aunque, como la mera legibilidad por línea probablemente lo sea para el programador. El comprador va a hacer un montón de esos bajos, bajos pagos; y el programador va a leer un montón de esas líneas individualmente legibles.

Este compromiso precede a los lenguajes de programación. Si estás acostumbrado a leer novelas y artículos de periódico, tu primera experiencia de leer un artículo de matemáticas puede ser desalentadora. 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 parecer que lo es. 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 los matemáticos tuvieron que hacer antes de que evolucionaran notaciones concisas), no serían más fáciles de leer, porque el artículo crecería al tamaño de un libro.

¿Hasta Qué Extremo?

Un número de personas han rechazado la idea de que la concisión = poder. 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 = poder? Porque claramente la concisión es una gran parte de para qué están los lenguajes de alto nivel. Si no es todo para lo que están, entonces, ¿para qué más están, y cuán importantes son, relativamente, estas otras funciones?

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

La hipótesis con la que comencé fue que, excepto en ejemplos patológicos, pensé que la concisión podría considerarse idéntica al poder. Lo que quise decir fue 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. No estoy ni siquiera seguro de eso, en realidad.

Lenguajes, no Programas

Debemos tener claro que estamos hablando de la concisión de los lenguajes, no de programas individuales. Ciertamente es posible que programas individuales se escriban de manera demasiado densa.

Escribí sobre esto en On Lisp. Una macro compleja puede tener que ahorrar muchas veces su propia longitud para ser justificada. Si escribir alguna macro complicada pudiera 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 usas más de una vez. Pero eso aún podría ser un mal movimiento, porque las definiciones de macros son más difíciles de leer que el código ordinario. Podrías tener que usar la macro diez o veinte veces antes de que produjera una mejora neta en la legibilidad.

Estoy seguro de que cada lenguaje tiene tales compromisos (aunque sospecho que las apuestas se vuelven más altas a medida que el lenguaje se vuelve más potente). Cada programador debe haber visto código que alguna persona ingeniosa ha hecho marginalmente más corto usando trucos de programación dudosos.

Así que no hay discusión sobre eso-- al menos, no de mi parte. Los programas individuales pueden ser ciertamente 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 razón por la que es difícil imaginar que un lenguaje sea demasiado conciso es que si hubiera alguna forma excesivamente compacta de expresar algo, probablemente también habría una forma más larga. Por ejemplo, si sientes que los programas de Lisp que usan muchas macros o funciones de orden superior son demasiado densos, podrías, si lo prefieres, escribir código que sea isomórfico a Pascal. Si no quieres expresar el 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 puedo pensar en ejemplos de inmediato, estoy interesado en la pregunta de si un lenguaje podría ser demasiado conciso. ¿Hay lenguajes que te obliguen a escribir código de una manera que sea constrictiva e incomprensible? Si alguien tiene ejemplos, estaría muy interesado en verlos.

(Recordatorio: Lo que busco son programas que sean muy densos según la métrica de "elementos" esbozada anteriormente, no meramente programas que sean cortos porque se pueden omitir delimitadores y todo tiene un nombre de un carácter).