sábado, 11 de junio de 2022

Breve historia de los lenguajes de programación


Índices:Historia de la Informática

Otro que escribí en 2018, más vale tarde que nunca para los que gusten de un poco de historia de la informática.


Los ordenadores se clasifican por generaciones, la primera engloba a los primeros electromecánicos y a los de lámparas, la segunda los de transistores, la tercera en adelante la de los chips en función de su escala de integración. Los lenguajes también han tenido sus épocas, en general pasamos de una primera con su nacimiento, la etapa cuando el GOTO era el rey, los lenguajes estructurados, los lenguajes 4GL, y ahora se viven los lenguajes distribuidos. Siempre los lenguajes han seguido la evolución de los ordenadores, ya que hasta que no se disponía de recursos o de potencia de cálculo suficiente no era posible desarrollar ciertas características de los lenguajes.

Primeros ordenadores

Yo considero que es un error englobar en la primera generación todas las máquinas iniciales, ya que opino que debería haber una generación cero con las primeras máquinas en las que el programa estaba completamente separado de los datos, en lo que hoy se conoce como arquitectura Harvard , y empezar la primera generación en el EDVAC y el ILLIAC que fueron los primeros en adoptar lo escrito por Von Newman sobre el programa almacenado en memoria, usando la arquitectura que hoy se denomina Newman-Eckert-Mauchly, o arquitectura Princeton. Hoy día los microcontroladores como los PIC o los Atmel usan arquitectura Harvard, ya que el programa se graba en una memoria (usualmente flash) y permanecen indefinidamente, mientras los datos están en RAM y se pierden al apagar, pero su potencia de proceso y su rico lenguaje máquina hace que esto no sea ya una diferencia importante, solo es cuestión del tipo de memoria adecuada para cada uso.

Las primeras máquinas eran calculadoras y poco más, con las que se podían efectuar muchos cálculos repetitivos y siempre correctos (aunque en las primeras no siempre era así). Las Z las ideó Konrad Zuse pues trabajaba realizando el cálculo de las formas de las alas de los aviones, lo que representa calcular muchos datos usando siempre la misma fórmula, solo cambiaban algunos parámetros. El ENIAC se dedicó inicialmente a compilar tablas para la artillería, en función del tamaño del proyectil (proporcional al calibre del cañón), de su peso, de la carga de pólvora que se le introduce y del ángulo de disparo respecto a la horizontal, se calculaba el alcance del proyectil, y con esto se generaban tablas y más tablas de datos que se imprimían como manuales de tiro para cada cañón concreto.

Cinta de papel perforada (fuente: uvaciberperiodismo)
Las Z y casi todo el resto de los ordenadores usaban una unidad de cinta de papel en la que estaban las instrucciones a ejecutar, mientras que los datos se almacenaban en la memoria del aparato, por contra que en el ENIAC los datos estaban en la memoria igualmente, pero se usaba unos paneles en los que se cableaban las instrucciones a realizar, la máquina disponía de una serie de circuitos (sumadores, restadores, multiplicadores, divisores, etc), mediante el panel se cableaban las salidas de un circuito con la entrada de otro, seleccionando el origen de la información adicional si era necesario. Las Z eran máquinas electromecánicas binarias, el ENIAC usaba tubos de vacío, pero montados para trabajar en decimal, otras como la británica Harwell Computer usaban tubos decatrón que contaban bien de cero a nueve. En todas estas máquinas siempre se actuaba de la misma manera, al pedir desde el panel de control del aparato que se ejecute el programa, se leía una instrucción de la cinta del programa, se ejecutaba operando sobre los datos en memoria, copiándolos a otra ubicación o presentándolos en la salida, se avanzaba el lector de cinta una posición, se leía la nueva instrucción, se ejecutaba, y así sucesivamente hasta encontrar una instrucción especial que detenía la ejecución.

El lector de cinta no podía retroceder ni saltar, pero pronto se dieron cuenta de que esto era necesario incluso para sencillas ecuaciones, por ejemplo el cálculo de una raíz cuadrada puede dar un resultado real o imaginario, por lo que recurrieron a soluciones ingeniosas como usar más de un lector de cinta, el programa se empezaba a ejecutar en la primera cinta, pero en función del valor de algún registro de memoria se podía hacer que continuara en otra unidad, ejecutando así las primeras instrucciones condicionales. Se llegó a maquinas con hasta16 unidades de cinta para ejecutar los programas, pero con la limitación de no poder usar por ejemplo bucles. Se cargaban las cintas de papel, se introducían los datos en la memoria, y se lanzaba la ejecución del programa.

El imperio del código máquina


Con el programa almacenado en memoria esto cambió, ahora el ordenador podía ejecutar la instrucción en una posición de memoria, y en función de su evaluación saltar a la posición de memoria que se deseara (podía incluso modificarse a sí mismo, idea ya aportada por Von Newman y usada por ejemplo por Donald Knuth en "El arte de programar ordenadores"), mezclando en la memoria el programa y los datos se crearon los primeros ordenadores de propósito general auténticos, y los primeros Turing Completos (en este punto hay mucha controversia pues algunos afirman que las Z ya lo eran, pero sin instrucciones de salto yo personalmente no los incluiría en esta categoría). El cambio fue radical, ahora se escribía el programa en la cinta, se leía esta por completo en la memoria, se cargaban los datos en otras posiciones, y el ordenador empezaba a trabajar. Esto hizo necesario disponer de los primeros sistemas operativos muy rudimentarios, encargados de cargar los programas en la memoria de las maquinas.

Pronto se impusieron las unidades lectoras de tarjetas perforadas sobre las de cinta de papel, más cómodas de manejar, en cada tarjeta se escribía una dirección de memoria y su contendido, que era una sola operación o un solo dato, por lo que en caso de necesitar cambiar algo solo había que reemplazar una tarjeta y no volver a teclear todo el programa o cortar la cinta, eliminar un trozo y empalmar otro.

Se seguía trabajando igual, se perforaban las tarjetas con el programa, siempre en código máquina directamente, se añadían los datos, lo que podía hacerse directamente en la memoria o mediante otras tarjetas, y se ejecutaba el programa.

Nuestro cerebro es peor manejando números que palabras, y el número de instrucciones fue creciendo poco a poco, desde las 6 que podía efectuar el Z1 ya había que manejar decenas de ellas, por lo que pronto se desarrollaron nemotécnicos para los códigos de las instrucciones, los programas se empezaron a desarrollar en ensamblador, pero se traducían a código máquina manualmente.

Con el aumento de potencia de las máquinas fue posible desarrollar los primeros programas ensambladores, uno de los primeros fue introducido en el ordenador más vendido del momento, el IBM 650, desarrollado en los años 50, y se denominó SOAP (Symbolic Optimized Assembler Program), el programa se escribía en tarjetas perforadas, y luego se cargaban dos grupos de tarjetas, primero el programa ensamblador, luego las tarjetas con el programa en lenguaje ensamblador. El S.O. leía las tarjetas del ensamblador y lo cargaba en memoria, se ejecutaba el programa y este leía una tarjeta de código, la ensamblaba y luego grababa una tarjeta con el resultado. De esta forma se disponía del programa listo para su ejecución en código máquina de forma más sencilla.

A partir de 1951 surgieron varios lenguajes ensamblador con intentos de hacer uno universal, como el Regional Assembly Language de 1951, pero ante la disparidad de ordenadores del momento no funcionó. IBM por su lado lanzó en 1952 su lenguaje ensamblador denominado Autocoder, que tuvo varias versiones, intentando que se pudiera ejecutar en todos sus modelos pero sin conseguirlo completamente, por lo que cada Autocoder era específico de un ordenador o familia de ellos.

Primeros intentos

El Código Maquina y el Ensamblador fueron los reyes en la primera generación, en los 60 con la segunda generación de ordenadores se popularizaron los primeros lenguajes, desarrollados en los años 50, de los que voy a mencionar los más importantes en su orden cronológico de aparición. Hay que tener en cuenta que inicialmente se siguió utilizando mayoritariamente el Ensamblador, ya que permitía utilizar mejor los escasos recursos de las máquinas de las primeras generaciones.

Esta primera generación de lenguajes marcó los inicios de la computación moderna, marcó el gran hito de conseguir la independencia del lenguaje sobre la máquina en la que se ejecuta, y fijó las pautas de los posteriores lenguajes. Todos son leguajes imperativos, y todos evolucionaron haciéndose cada vez más estructurados, aunque mantienen el uso del GOTO

Aunque el auténtico pionero fue Zuse, en su Plankalkül , que podemos considerar como el primer lenguaje de programación, creado entre 1942 y 1946, que describió en un artículo en 1948, pero su especificación solo se publicó en 1972, como todo lo relacionado con Zuse siempre se tardó en reconocer sus méritos. Era un lenguaje bastante completo, con manejo de tipos de datos o de matrices, pero no era estructurado ni disponía de instrucciones de entrada/salida.

A principios de los 50 se empezó a experimentar con los primeros lenguajes primitivos, Grace Murray Hopper , una de las mayores figuras de la historia de la computación, si no la mayor, desarrolló el Sistema A-0 (Arithmetic Language version 0), considerado el primer lenguaje de alto nivel. Por aquella época ya se habían desarrollado muchas rutinas de código, que eran usadas añadiendo las tarjetas de esas rutinas a las de tu programa, la idea de Grace fue disponer de una gran librería de rutinas de código desarrolladas. Un programa en A-0 consistía en una secuencia de llamadas a esas rutinas junto a sus parámetros de entrada, y alguna instrucción adicional. Esto se perforaba en las tarjetas, y luego se usaban un conjunto de tres grupos de tarjetas, primero un primitivo compilador (poco más que un pequeño linker), que se cargaba en la memoria, seguían las tarjetas del "programa", que el "compilador" convertía en código máquina, y por fin las tarjetas con todas las rutinas que se podían usar, de las que se copiaban solo las necesarias para ejecutar el programa. De esta forma en la salida se encontraba el programa listo para usar.

No hay que menospreciar esto que aparenta ser tan sencillo, pues dependiendo de las rutinas que se cargaran los saltos dentro del programa debían efectuarse a unas u otras posiciones de memoria, por lo que no era tan directo compilar el programa, usualmente se empleaban varias unidades de cinta magnética para almacenar los datos intermedios, y mezclar todo al final en otra cinta, desde donde se podían enviar a la perforadora de tarjetas.

El A-0 evolucionó y se amplió, fue seguido por el A-1, el A-2 (cuyo fuente se publicó considerándose el primer programa de código abierto), evolucionó mucho para convertirse en el A-3 orientado a las matemáticas, en dos variantes denominadas A-3 o ARITH-MATIC, y AT-3 o MATH-MATIC), y por fin el B-0 o FLOW-MATIC, de 1955, orientado a la gestión y precursor directo del COBOL. 

Por otro lado, en 1953 surgió Speedcoding , desarrollado por John Backus. Era muy similar al A-0, pero enfocado a las matemáticas en punto flotante, ya que lo desarrolló para simplificar la escritura de programas para el ordenador científico IBM 701, en el que se solicitó un programa de cálculo de posiciones de estrellas para la década de los años 50, necesario para el manejo por los astrónomos. El leguaje era complicado de manejar, funcionaba bien, pero los programas que desarrollaba eran mucho más lentos que los escritos en código máquina, hasta 20 veces más lentos, lo que no lo hacía muy operativo.

1957 FORTRAN

El primer lenguaje históricamente fue el hoy casi olvidado FORTRAN (Formula-Traslation o traductor de fórmulas), orientado a la computación matemática, desarrollado por otro de los grandes padres de la computación, John Backus en la IBM, a partir de 1953 para el ordenador IBM-704, orientado hacia aplicaciones científicas, a partir de su anterior SpeedCoding. El primer compilador de FORTAN convertía el fuente en ensamblador SOAP, y luego este se ensamblaba y convertía en código máquina. El lenguaje fue lanzado para los clientes de IBM en 1957. El FORTRAN fue ampliamente utilizado en los años 60 para operaciones de cálculo matemático y de ingeniería, marcó el desarrollo del ALGOL y en su sintaxis se inspiró el BASIC .

Como particularidad un programa en FORTAN no tiene en cuenta los espacios en blanco salvo en las cadenas, provocando un dilema de compilación clásico, un bucle tiene la forma DO ETIQUETA VARIABLE = INICIAL . FINAL en donde ETIQUETA es la etiqueta que marca donde termina el bucle, VARIABLE la que lleva la cuenta del bucle, INICIAL y FINAL son los valores por los que iterará. Podemos escribir DO 10 I = 1 . 10 o bien DO10I=1.10 siendo equivalentes, pero también podemos escribir DO10I=1,10 aunque en este caso asignamos el valor 1,10 a la variable DO10I, haciendo que la compilación del programa sea un tanto "divertida", hasta el final de la línea no sabes cómo interpretar la sentencia, por lo que no es posible ir montando los habituales arboles de exploración de los compiladores.

1958 LISP


El segundo lenguaje de la época fue LISP (List Processor, procesados de listas), que todavía es usado. Está basado en un lenguaje anterior denominado IPL. Surgió poco después del FORTAN, pero inicialmente solo contaba con un intérprete y no disponía de compiladores. Especializado en lenguaje matemático inspirado en el Cálculo Lambda, muy bueno en el manejo de listas y usado desde sus inicios en Inteligencia Artificial. Aunque es un lenguaje poco usado fuera de ese campo, sigue vivo en lenguajes como PERL

1959 COBOL


El tercer lenguaje de la historia, que es todavía uno de los más usados hoy día, es COBOL (Common Business-Oriented Language, leguaje común orientado a los negocios), en cuyo diseño intervino un comité denominado CODASYL , aunque se asume que Grace Hopper es la diseñadora principal del lenguaje. Es el primer intento de desarrollar un lenguaje independiente completamente del ordenador en que se compila, pudiendo ejecutarse una vez compilado en otros ordenadores, y el primer intento de desarrollar en lenguaje natural en inglés. Esto ocasionó que los programas se puedan escribir en sentencias muy largas, escribiendo párrafos completos. Esto contrasta mucho con el escueto C, cuya sintaxis intenta hacer que se escriba lo mínimo posible para no perder tiempo escribiendo.

Al ser un lenguaje orientado a la gestión, contiene un extenso manejo de instrucciones de entrada/salida, manejo de ficheros y manejo de datos, incluyendo los primeros sistemas de manejo de ficheros indexados.

La primera versión de un compilador desarrollado en un lenguaje de alto nivel fue la que realizó Hooper para el COBOL, usando el lenguaje Flow-Matic.

1960 ALGOL


Aunque se comenzó a definir en 1958, hasta 1960 no se difundió. A partir del FORTAN, un equipo de científicos de la computación liderados por Backus desarrolló la especificación del cuarto lenguaje importante de esta época, el ALGOL (Algoritmic Languaje, lenguaje algorítmico), primer leguaje moderno de la historia y el que incluyó todo lo que hoy es un lenguaje imperativo, en el que por ejemplo se definió por primera vez el uso de bloques y las variables locales. 

El propio Backus introdujo la notación BNF para la definición de un lenguaje de ordenador, usada desde entonces. Aunque fue un lenguaje muy importante por las ideas que introdujo, su uso no se extendió ya que era difícil construir compiladores para él, ya que los ordenadores del momento no tenían recursos suficientes en general.

El primer compilador de un lenguaje desarrollado en el propio lenguaje que compilaba se desarrolló para ALGOL y se denominó NELIAC, se completó en 1958. Hoy día es poco conocido el ALGOL, pero no así su heredero directo, el lenguaje PASCAL.

1964 BASIC


Como vemos lo podemos considerar otro pionero por fechas, fue el primero que aprendí, mi muy querido BASIC del Spectrum, y luego trabajé con varias variantes del BASIC profesionalmente, por eso cuando puedo hacer algo hoy día prefiero usar Visual Basic, solo por nostalgia. 

BASIC proviene de Beginners' All-purpose Symbolic Instruction Code (Código simbólico de instrucciones de propósito general para principiantes). Fue diseñado en 1964 por John George Kemeny, Thomas Eugene Kurtz y Mary Kenneth Keller en el Dartmouth College en New Hampshire, Estados Unidos (por eso se conoce como el Dartmouth BASIC), fue creado como un medio para facilitar la programación en ordenadores a estudiantes (y profesores) que no fueran de ciencias.

Originalmente era un lenguaje interpretado, diseñado como herramienta de enseñanza, pero al tener una estructura sencilla de aprender, ser un lenguaje muy ligero y que un intérprete de BASIC es muy ligero, estuvo ampliamente disponibles en las microcomputadoras de los años 1970 y 1980.


1 comentario: