Loading...

LA CONCISIÓN ES PODER

Original

Mayo 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 problemas planteados por Revenge of the Nerds en la lista de correo LL1, Paul Prescod escribió algo que se me quedó grabado.

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

A primera vista, esto parece una cosa bastante condenatoria de decir sobre un lenguaje de programación. Según puedo ver, 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 que es un compromiso) que quisieras hacer. No está lejos de decir que el objetivo de Python no es ser efectivo como lenguaje de programación.

¿La concisión = poder? Esto me parece una pregunta importante, quizás la más importante para cualquiera interesado en el diseño de lenguajes, y sería útil abordarla directamente. Aún no estoy seguro 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 está lo suficientemente cerca como para que, excepto en ejemplos patológicos, puedas tratarlos como idénticos.

Me parece que la concisión es para lo que se diseñan los lenguajes de programación. Los ordenadores estarían igual de contentos si se les dijera qué hacer directamente en lenguaje 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 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 código fuente más pequeño es el propósito de los lenguajes de alto nivel, 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 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 ilegible.

Métricas

¿Pero pequeño en qué sentido? La medida más común del tamaño del código es el número de 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 debes 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 tampoco es muy buena; 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 dibujáramos un árbol que represente 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 fronterizos (¿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 a las comparaciones.

Esta métrica necesita ser desarrollada con más detalle, 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 ese no es, al menos para mí, su principal valor. El principal valor de la prueba de concisión es como 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?

(Por cierto, nada hace más patente que el viejo lugar común "todos los lenguajes son equivalentes" es falso que el diseño de lenguajes. Cuando estás diseñando un nuevo lenguaje, constantemente estás comparando dos lenguajes: el lenguaje si hago x, y si no lo hago, para decidir cuál es mejor. Si realmente fuera una pregunta sin sentido, podrías tirar una moneda al aire).

Apuntar a la concisión parece una buena manera de encontrar nuevas ideas. Si puedes hacer algo que haga que muchos programas diferentes sean más cortos, probablemente no sea una coincidencia: has probablemente descubierto una nueva abstracción útil. Incluso podrías poder escribir un programa que ayude buscando patrones repetidos en el código fuente. Entre otros lenguajes, aquellos con reputación de concisión serían los que habría que mirar para 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. Escribió que los programadores parecían generar aproximadamente la misma cantidad de código por día independientemente del lenguaje. Cuando lo leí por primera vez en mis veinte años, fue una gran sorpresa para mí y parecía tener enormes implicaciones. Significaba que (a) la única forma 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 pudiera obtener sobre el tema, 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, si bien generan el tipo de resultados que esperaba, tienden a usar problemas 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 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 la cosa equivocada.

La verdadera prueba de un lenguaje es qué tan bien puedes descubrir y resolver nuevos problemas, no qué tan 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 pésimos si no lo sabes. Cuando quieres descubrir la imagen a medida que la haces, como tienes que hacer con cualquier cosa tan compleja como una imagen de una persona, por ejemplo, necesitas usar un medio más fluido como el lápiz o el lavado de tinta o la pintura al óleo. Y de hecho, la forma en que se hacen las tapicerías y los mosaicos en la práctica es hacer una pintura primero y luego copiarla. (La palabra "cartoon" se usaba 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 precisas. 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 poderosos.

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

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

El estudio también aborda explícitamente un punto que solo era implícito en el libro de Brooks (ya que medía líneas de código depurado): los programas escritos en lenguajes más poderosos 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 confiar en 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 qué tan bien un lenguaje te permite pensar, y luego elegir/diseñar el lenguaje que se sienta 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 insoportablemente restrictivo programar en lenguajes sin macros, así como alguien acostumbrado a la escritura 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 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 que aceptarán cualquier trabajo donde puedan usar ese lenguaje, independientemente del dominio de aplicación.

Restricción

Creo que la mayoría de los hackers saben lo que significa que un lenguaje se sienta restrictivo. ¿Qué está pasando cuando sientes eso? Creo que es la misma sensación que tienes cuando la calle que quieres tomar está cerrada 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 está sucediendo aquí, creo, es que un lenguaje restrictivo es uno que no es lo suficientemente conciso. El problema no es simplemente que no puedas decir lo que planeabas. Es que el rodeo que el lenguaje te obliga a tomar es más largo. Prueba este experimento mental. Supongamos que hubiera algún programa que quisieras escribir y el lenguaje no te permitiera expresarlo de la manera que planeabas, sino que te obligara a escribir el programa de otra manera que fuera más corta. Para mí al menos, eso no se sentiría muy restrictivo. Sería como que la calle que querías tomar estuviera cerrada y el policía en la intersección te dirigiera a un atajo en su lugar. ¡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 de lo que tienes en tu cabeza. La restricción es principalmente falta de concisión. Entonces, 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 otras dos cualidades, regularidad y legibilidad. No estoy seguro de qué es la regularidad o qué ventaja, si la hay, tiene el código que es regular y legible sobre el código que es simplemente legible. Pero creo que sé a qué se refiere con legibilidad, y también creo que está relacionado con la concisión.

Tenemos que 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 probablemente sea 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 la tierra de Greenspun). El esfuerzo total de leer el 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 de que el poder lo es, pero ciertamente la concisión es un factor (en el sentido matemático; ver ecuación anterior) en la legibilidad. Así que ni siquiera puede tener 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. 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 isomórfico a la técnica muy exitosa de permitir que las personas paguen a plazos: en lugar de asustarlos con un alto precio inicial, les dices el bajo pago mensual. Los planes de pago a plazos son una pérdida neta para el comprador, aunque probablemente lo sean para el programador.

El comprador va a hacer muchos de esos pagos bajos y bajos; y el programador va a leer muchas de esas líneas individualmente legibles.

Este intercambio se remonta a antes de los lenguajes de programación. Si estás acostumbrado a leer novelas y artículos de periódicos, tu primera experiencia de leer un documento matemático puede ser desalentadora. Podría llevar 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 como tal. El documento matemático es difícil de leer porque las ideas son difíciles. Si se expresaran las mismas ideas en prosa (como los matemáticos tuvieron que hacer antes de que evolucionaran las notaciones concisas), no serían más fáciles de leer, porque el documento crecería hasta el tamaño de un libro.

¿En qué medida?

Varias 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 lo son, preguntar: ¿en qué medida la concisión = poder? Porque claramente la concisión es una gran parte de lo que son los lenguajes de alto nivel. Si no es todo lo que son, entonces, ¿qué más son y qué tan importantes, en comparación, son estas otras funciones?

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

La hipótesis con la que comencé era que, excepto en ejemplos patológicos, pensaba que la concisión podría considerarse idéntica al poder. Lo que quería decir era que en cualquier lenguaje que alguien diseñaría, serían idénticos, 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 se escriban con demasiada densidad.

Escribí sobre esto en On Lisp. Un macro complejo puede tener que ahorrar muchas veces su propia longitud para justificarse. Si escribir algún macro complicado pudiera ahorrarle diez líneas de código cada vez que lo use, y el macro en sí tiene diez líneas de código, entonces obtienes un ahorro neto en líneas si lo usas más de una vez. Pero eso aún podría ser una mala jugada, porque las definiciones de macros son más difíciles de leer que el código ordinario. Tal vez tengas que usar el macro diez o veinte veces antes de que produzca una mejora neta en la legibilidad.

Estoy seguro de que todos los lenguajes tienen tales intercambios (aunque sospecho que las apuestas se vuelven más altas a medida que el lenguaje se vuelve más poderoso). Todos los programadores deben 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 ciertamente 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 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 sintiera que los programas de Lisp que usan muchos 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 ningún ejemplo de inmediato, me interesa 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 apretada e incomprensible? Si alguien tiene ejemplos, estaría muy interesado en verlos.

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