lunes, 17 de septiembre de 2012

Compilación y bibliotecas (parte 1): ¿por qué?

Aprender a programar es difícil. Involucra muchas cosas. Por un lado están la lógica, el nivel de abstracción, los conceptos, los paradigmas; todas esas cosas inherentes a la programación en sí, e independientes del lenguaje. Por otro lado está el mismísimo lenguaje, esa oscura maraña de reglas, en un inglés abreviado y poco amigable para el estudiante, donde tratamos de aplicar todo lo primero para poder ver sus frutos. Mediante mis proyectos PSeInt y ZinjaI intento contribuir a simplificar el aprendizaje en ambos frentes. Siempre digo que aprender las dos cosas al mismo tiempo es demasiado difícil. Para poder separar e ir por partes es que nace el pseudocódigo. El pseudocódigo sería una herramienta didáctica para aprender y practicar lo conceptual sin lidiar con el lenguaje. Allí es donde espero que PSeInt dé una mano, permitiendo hacer alguna práctica más real y entretenida de esa primera parte. Una vez dado ese primer paso, se pasa a estudiar un lenguaje real, y allí es donde (si el lenguaje resulta ser C++) ZinjaI intenta contribuir. Pero el problema acá es que deliberadamente oculté un tercer componente muy muy importante en esto de aprender a programar, que es el compilador. Un lenguaje sin un compilador no es más que ideas en el aire, intangibles y poco prácticas. Es el compilador (o intérprete) el que lo trae a la vida, el que hace el nexo entre ese lenguaje y el conjunto hardware+sistema operativo que representa el mundo real para el programa.

Y entonces, otra prueba importante a superar en el proceso de convertirse en un buen programador es la de conocer y entender en cierto grado el proceso de compilación, para poder hacer cosas como crear o reutilizar bibliotecas por ejemplo. Pero un IDE amigable para el estudiante como intenta ser ZinjaI, lo que busca es ocultar ese funcionamiento y evitarnos requerir ese conocimiento, para poder concentrarnos en cualquier otra cosa casi exclusivamente. Y esto está bien cuando uno aprende, ya que otra vez permite separar las cosas en etapas y atacarlas de a una en un plan más o menos razonable: primero fundamentos, después el lenguaje, después las bibliotecas estándar, etc. Pero lo que suele ocurrir en la práctica es que esa formación importante acerca del proceso de compilación que al comienzo pospusimos con buen criterio jamás se retoma. Aunque aparezca en la planificación de contenidos de muchos cursos de programación, suele ocupar o nulo poco tiempo realmente en la práctica, y los problemas relacionados se resuelven sólo bajo demanda, siendo en general muy escasa esa demanda, e importando mucho más la solución en concreto que el porqué o el fundamento. Es decir, mientras tengamos un asistente que con tres clicks en siguiente nos configure el proyecto para que dándole a F9 el programa se ejecute todo está bien y nadie o muy pocos se preguntan qué está ocurriendo detrás.

Creo sin la menor duda que un buen programador debe conocer esos mecanismos y debe saber qué está haciendo el IDE por él, aunque igual se lo deje hacer al IDE por comodidad y velocidad. Esto le permite controlar detalles más finos, entender mejor los errores, tomar decisiones de diseño más amplias en proyectos realmente complejos, valorar mejor la elección de tal o cual IDE, adaptarse a otros ambientes o plataformas de trabajo, etc. Y veo a través de las consultas que recibo (tanto consultas como docente de muchos alumnos, como las consultas de usuarios de ZinjaI o PSeInt en general) que hay mucho desconocimiento y también desinterés en este aspecto. Es por eso que pretendo escribir algunos posts con contenido básico de divulgación al respecto. La idea es empezar hablando de la compilación en general (previa justificación, y diferenciación entre compilación e interpretación), para luego, tomando C++ y el compilador gcc como caso de estudio, presentar brevemente las etapas del proceso de compilación, las alternativas que ofrece el mismo. El tema final será el de las bibliotecas. Preguntas muy recurrentes de usuarios de ZinjaI son ¿cómo hago una biblioteca?, o más frecuentemente ¿cómo uso tal biblioteca?. Me niego a entregar una receta simple y directa, paso a paso y sin justificación alguna, porque creo que eso tiene patas muy cortas. Voy a escribir algunos posts (creo que 2) generales sobre el tema sentando las bases, con contenido independiente del uso de ZinjaI o cualquier otro IDE; y en otro último tendrán su receta simple y directa. Para ese entonces, espero que los pasos mágicos de la receta se entiendan sin necesidad de explicitar los motivos, y resulten más obvias las potenciales variaciones para distintos escenarios.

Para no cerrar este primer post habiendo prometido bastante pero explicado absolutamente nada, voy empezar comentando un dibujo que armé y que me gusta mostrarles a mis alumnos la primer clase para que sepan donde van a estar parados en esto de la programación y los lenguajes, y al que vuelvo más de una vez durante el cursado al introducir nuevos conceptos que tocan directa o indirectamente estas ideas de interpretación y compilación.


La idea es la siguiente: nosotros, los humanos, de este lado del mundo (y a la izquierda en el dibujo) hablamos español, mientras que los procesadores, por el otro (a la derecha en el dibujo) hablan, digamos, código de máquina. Y si cada uno habla como quiere no nos entendemos. Entonces, hay todo un espacio que atravesar, un trayecto que recorrer para lograr una comunicación, para poder decirle a la computadora qué queremos que haga y que nos entienda. Si tuviéramos que hacer todo el trayecto de una nosotros humanos aprendices hasta llegar al código de máquina nos llevaría mucho tiempo, y sería tan complejo que solo podríamos resolver problemas relativamente simples. Entonces, lo que ocurre en realidad es que se divide el esfuerzo. En algún punto en medio del camino está el lenguaje de programación (la rayita azul), que es mucho más rígido y difícil que el lenguaje natural de los humanos, pero que también es mucho más simple y entendible que el lenguaje de máquina. Si este punto está más cerca del lado del humano se dice que es un lenguaje de alto nivel, mientras que si está más del lado de la computadora se dice que es de bajo nivel. Pero la idea es que el programador aprendiz debe comprometerse a avanzar en este camino solo hasta ese punto. Entonces, dará las instrucciones en un lenguaje mucho mejor, el lenguaje de programación, pero que todavía no es el de la computadora. La gracia es que, por suerte, ese lenguaje puede ser convertido al que le gusta a la computadora automágicamente por un programa. Ese programa será el intérprete o el compilador, según el lenguaje. Y así, mitad del camino es responsabilidad del estudiante, la otra mitad del intérprete/compilador, y ahora la travesía es más realizable.

Claro está que el compilador no sale de la nada, sino que es un programa más. Hay un dilema de tipo huevo-gallina allí, y tenemos que pensar que al principio los programadores debían hacer todo el recorrido ellos mismos, pero con el tiempo empezaron a cansarse y a generar herramientas para ayudarse en ese camino. Luego de mucha evolución, de esfuerzos de grandes informáticos, de ideas brillantes y de ideas que no funcionaron, del envión que dio el avance del hardware, y de mil cosas más, hoy en día tenemos modernos compiladores escritos en lenguajes de alto nivel y compilados con versiones viejas de ellos mismos, elevando la complejidad y las capacidades propias y de sus lenguajes de una forma espectacular, increíblemente enmarañada y maravillosamente natural, todo a la vez.

Este tema continúa en Compilación y bibliotecas (parte 2): Intérpretes vs Compiladores

No hay comentarios:

Publicar un comentario