Programación de abajo hacia arriba
1993
(Este ensayo proviene de la introducción aOn Lisp .)
Es un principio de larga data del 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á de la etapa en la 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 deberá 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 tenga 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 sola unidad.
Los programadores experimentados de 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 -- cambiar el lenguaje para adaptarlo al problema. En Lisp, no solo escribes tu programa hacia el lenguaje, sino que 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 finalmente descansa a lo largo de las montañas y los 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 encajan bien, terminas con código 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 programa único y 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 extraes las partes que son meramente contabilidad, lo que queda es mucho más corto; cuanto más construyas el lenguaje, menos distancia tendrás que recorrer de arriba hacia abajo hasta él. Esto trae varias ventajas:
-
Al hacer que el lenguaje haga más trabajo, el diseño de abajo hacia arriba produce programas más pequeños y ágiles. Un programa más corto no necesita dividirse en tantos componentes, y menos componentes significa programas más fáciles de leer o modificar. Menos componentes también significa menos conexiones entre componentes y, por lo tanto, menos posibilidades de errores allí. Así como los diseñadores industriales se esfuerzan por reducir el número de piezas móviles en una máquina, los programadores experimentados de 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 hayas adquirido un gran sustrato de utilidades, escribir un nuevo programa puede llevar solo una fracción del esfuerzo que requeriría si tuvieras que empezar 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 pide al lector que comprenda un operador de propósito general; una instancia de abstracción funcional pide al lector que comprenda una subrutina de propósito especial. [1]
-
Debido a que te obliga a estar siempre atento a los 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 tienen una forma similar, te verás llevado 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 punto en lenguajes distintos de Lisp. Siempre que ves funciones de biblioteca, está ocurriendo un diseño de abajo hacia arriba. Sin embargo, Lisp te da poderes mucho más amplios en este departamento, y aumentar el lenguaje juega un papel proporcionalmente mayor en el estilo Lisp, tanto es así 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 Lisp sugiere una forma más alegre de expresar esta ley: a medida que disminuye el tamaño del grupo, aumenta la productividad de los programadores individuales. Un grupo pequeño gana, en términos relativos, simplemente porque es más pequeño. Cuando un grupo pequeño también aprovecha las técnicas que Lisp hace posibles, puede ganar por completo.
Nuevo: Descarga 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, véase la Sección 4.8.