PROGRAMACIÓN DE ABAJO HACIA ARRIBA
OriginalEnero de 1993
(Este ensayo es de la introducción aOn Lisp.)
Es un principio de larga data en el estilo de programación que los elementos funcionales de un programa no deben ser demasiado grandes. Si algún componente de un programa crece más allá del punto en que es fácilmente comprensible, se convierte en una masa de complejidad que oculta errores tan fácilmente como una gran ciudad oculta fugitivos. Tal software será difícil de leer, difícil de probar y difícil de depurar.
De acuerdo con este principio, un programa grande debe dividirse en partes, y cuanto más grande sea el programa, más debe dividirse. ¿Cómo se divide un programa? El enfoque tradicional se llama diseño de arriba hacia abajo: dices "el propósito del programa es hacer estas siete cosas, así que lo divido en siete subrutinas principales. La primera subrutina tiene que hacer estas cuatro cosas, así que a su vez tendrá cuatro de sus propias subrutinas", y así sucesivamente. Este proceso continúa hasta que todo el programa tiene el nivel adecuado de granularidad: cada parte lo suficientemente grande como para hacer algo sustancial, pero lo suficientemente pequeña como para ser entendida como una unidad única.
Los programadores experimentados en Lisp dividen sus programas de manera diferente. Además del diseño de arriba hacia abajo, siguen un principio que podría llamarse diseño de abajo hacia arriba-- cambiando el lenguaje para adaptarse al problema. En Lisp, no solo escribes tu programa hacia el lenguaje, también construyes el lenguaje hacia tu programa. Mientras escribes un programa, puedes pensar "Desearía que Lisp tuviera tal o cual operador." Así que vas y lo escribes. Después te das cuenta de que usar el nuevo operador simplificaría el diseño de otra parte del programa, y así sucesivamente. El lenguaje y el programa evolucionan juntos. Como la frontera entre dos estados en guerra, el límite entre el lenguaje y el programa se dibuja y redibuja, hasta que eventualmente se asienta a lo largo de las montañas y ríos, las fronteras naturales de tu problema. Al final, tu programa parecerá como si el lenguaje hubiera sido diseñado para él. Y cuando el lenguaje y el programa se ajustan bien entre sí, terminas con un código que es claro, pequeño y eficiente.
Vale la pena enfatizar que el diseño de abajo hacia arriba no significa simplemente escribir el mismo programa en un orden diferente. Cuando trabajas de abajo hacia arriba, generalmente terminas con un programa diferente. En lugar de un solo programa monolítico, obtendrás un lenguaje más grande con operadores más abstractos, y un programa más pequeño escrito en él. En lugar de un dintel, obtendrás un arco.
En el código típico, una vez que abstraes las partes que son meramente contabilidad, lo que queda es mucho más corto; cuanto más alto construyas el lenguaje, menos distancia tendrás que recorrer de arriba hacia abajo. Esto trae varias ventajas:
Al hacer que el lenguaje haga más del trabajo, el diseño de abajo hacia arriba produce programas que son más pequeños y ágiles. Un programa más corto no tiene que dividirse en tantos componentes, y menos componentes significan programas que son más fáciles de leer o modificar. Menos componentes también significan menos conexiones entre componentes, y por lo tanto menos posibilidades de errores allí. A medida que los diseñadores industriales se esfuerzan por reducir el número de piezas móviles en una máquina, los programadores experimentados en Lisp utilizan el diseño de abajo hacia arriba para reducir el tamaño y la complejidad de sus programas.
El diseño de abajo hacia arriba promueve la reutilización de código. Cuando escribes dos o más programas, muchas de las utilidades que escribiste para el primer programa también serán útiles en los siguientes. Una vez que has adquirido un gran sustrato de utilidades, escribir un nuevo programa puede llevar solo una fracción del esfuerzo que requeriría si tuvieras que comenzar con Lisp puro.
El diseño de abajo hacia arriba hace que los programas sean más fáciles de leer.
Una instancia de este tipo de abstracción le pide al lector que entienda un operador de propósito general; una instancia de abstracción funcional le pide al lector que entienda una subrutina de propósito específico. [1]
Debido a que te hace estar siempre atento a patrones en tu código, trabajar de abajo hacia arriba ayuda a aclarar tus ideas sobre el diseño de tu programa. Si dos componentes distantes de un programa son similares en forma, te llevará a notar la similitud y quizás a rediseñar el programa de una manera más simple.
El diseño de abajo hacia arriba es posible hasta cierto grado en lenguajes que no son Lisp. Siempre que veas funciones de biblioteca, el diseño de abajo hacia arriba está ocurriendo. Sin embargo, Lisp te da poderes mucho más amplios en este departamento, y aumentar el lenguaje juega un papel proporcionalmente más grande en el estilo de Lisp-- tanto que Lisp no es solo un lenguaje diferente, sino una forma completamente diferente de programar.
Es cierto que este estilo de desarrollo se adapta mejor a programas que pueden ser escritos por grupos pequeños. Sin embargo, al mismo tiempo, extiende los límites de lo que puede hacer un grupo pequeño. En The Mythical Man-Month, Frederick Brooks propuso que la productividad de un grupo de programadores no crece linealmente con su tamaño. A medida que aumenta el tamaño del grupo, la productividad de los programadores individuales disminuye. La experiencia de la programación en Lisp sugiere una forma más optimista de expresar esta ley: a medida que disminuye el tamaño del grupo, la productividad de los programadores individuales aumenta. Un grupo pequeño gana, relativamente hablando, simplemente porque es más pequeño. Cuando un grupo pequeño también aprovecha las técnicas que Lisp hace posibles, puede ganar de manera contundente.
Nuevo: Descargar On Lisp Gratis.
[1] "Pero nadie puede leer el programa sin entender todas tus nuevas utilidades." Para ver por qué tales afirmaciones suelen ser erróneas, consulta la Sección 4.8.