duduromeroa.com

RESEÑA: CAPÍTULO I

Programación orientada a objetos, una revisión histórica y técnica: primeras sintaxis de lenguajes de programación


Por Eduardo Romero | Guayaquil, Ecuador

www.duduromeroa.com, animación, lector, gif


#programación, #orientaciónObjetos, #sintaxis, #programación


Un lenguaje para hablar con máquinas, pero pensando cómo máquina

Desde la década de 1960 la humanidad necesitó a usar sistemas de cómputo junto con lenguajes de programación.

Los lenguajes de programación son "el idioma (resumido y abstracto) que las computadores interpretan para ejecutar acciones entre máquina y usuario". Para James Larus en Ciencias de la computación todo es abstracción; es decir, solo podemos manipular una pequeña porción representada en código o interfaces táctiles, mientras que una gran parte (la más importante y trascendente) estará oculta.

"Los lenguajes de programación son los puentes que abarcan el abismo entre el programador y la máquina."

Larus explica: "La informática es (...) abstracción sobre la abstracción, siendo eso una virtud como una necesidad. Los sistemas operativos, las bases de datos y los compiladores son programas muy complejos formados por cuarenta años de teoría y desarrollo". "En su mayor parte, los programadores necesitan poca o ninguna comprensión de la lógica o estructura interna de un software para usarlo de manera productiva. La mayoría de las veces, la ignorancia es una bendición." Scott (2006, prefacio).

Compilador

Software que convierte código comprendido por humanos a código binario comprensible para máquina. Esa conversión ocurre cada vez que las instrucciones diseñadas por humanos deben ser ejecutadas por la máquna.

La informática es abstracción sobre la abstracción. Es decir, será necesario esconder una complejidad para poder manipularla.


Según esa moderna necesidad de manipular la realidad mediante informática, apareció el lenguaje Plankalkül (Plan de cálculo, en español) teorizado por el alemán Konrad Zuse en 1940. Plankalkül fue el primer intento moderno en documentar instrucciones formales para un sistema mecánico lógico, pero solo era teoría, no fue adoptado en Ciencias. Solo las urgentes necesidades de producción en empresas militares y en la industria aeroespacial promovieron en Estados Unidos crear y usar nuevas propuestas en lenguajes de programación estructurada (donde las instrucciones seguían una ruta de 'ir a la línea línea X - vuelva a la línea Y'), como ForTran (entre 1950 y 1957) y COBOL (1959), los primeros y más extendidos en universidades e industria.

Ada Lovelace

Plankalkül no fue el primer intento. En 1843 la inglesa Ada Lovelace (1815-1852) escribió en papel un algoritmo llamado 'Notación G' como ejercicio de lógica para un cálculo matemático.


Ejemplos de lenguajes Plankalkul, ForTran y COBOL

###########
_En Plankalkül. Suma el valor 1 con el valor que contenga el literal Z

  | Z + 1 ⇒ Z 
V | 1       1
S | 1.n 1.n 1.n

###########
_En ForTran. Crea literales A y B y les asigna valores para luego mostrarlos en pantalla; y luego, cerrar el código.

PROGRAM SUMA
	INTEGER A, B, RESULT
	A = 10
	B = 5
	RESULT = A + B
	PRINT *, RESULT
END

###########
_En COBOL. Nombra el bloque de código como 'Suma' y crea literales A y B y les asigna valores de solo dos dígitos para luego sumarlos mediante el operador ADD, y cuyo resultado debe ser mantenido en la memoria del sistema para luego mostrarlos en pantalla.

IDENTIFICATION DIVISION.
	PROGRAM-ID. SUMA.
	DATA DIVISION.
	WORKING-STORAGE SECTION.
		01 A PIC 9(2) VALUE 10.
		01 B PIC 9(2) VALUE 5.
		01 RESULT PIC 9(3).

	PROCEDURE DIVISION.
	ADD A TO B GIVING RESULT
	DISPLAY RESULT
STOP RUN.

Pero en 1950 los lenguajes informáticos requerían de un correcto descriframiento de signos algebráicos y palabras clave poco intuitivas de reconocer. Es decir, el humano estaba obligado a pensar cómo una máquina. Si bien era útil para el sistema, resultaba poco agraciado para el humano que debía manipular esos lenguajes. Abajo, un extracto (el código es más extenso) de lenguaje Ensamblador (Assembly) con instrucciones directas al chip de cómputo para calcular el máximo común divisor de dos enteros.

Nótese la extrema abreviación de las palabras en forma mnemotécnica: lw a0,28 (sp) quiere decir 'cargar palabra' –o load word- en el índice de memoria 28), o la instrucción jal significa 'salta y vincula' -jump and link-. Tomado de Scott (2006, p. 3).

En Assembly. Scott (2006)

addiu sp, sp, - 32
sw ra, 20 (sp) b C
jal getint
nop
jal getint
sw v0,28 (sp)

lw a0,28 (sp)

move V1,V0
beq 20, vO, D
sit at, v1, ao
A: beq at, zero, B
пор

Entre 1950 y 1970 lo que más importaba de los lenguajes de programación era su eficiencia. Es por eso que las primeras sintaxis de programación eran elaboradas desde la utilidad de la máquina, y no tanto para facilitar la comprensión de su operador humano.

Sintaxis y semántica

En lenguajes de programación la sintaxis es la forma en que ciertas palabras y signos deben ser combinados durante la escritura del código. El objetivo es respetar esa sintaxis para crear significados que el intérprete virtual pueda ejecutar. Por ejemplo, en el lenguaje Javascript es erróneo usar variable = 10; (y peor, no nombrarlo) para vincular un dato a un área de memoria del sistema. Lo correcto es usar la clave var y darle un nombre identificador, como en var numero = 10;. Así mismo, la semántica es qué significado tiene esa expresión de sintaxis y cómo se relaciona con el resto del código.


Ese enfoque debía cambiar con urgencia. La principal razón es que un humano que crea código no siempre será el mismo quién lo releea, lo actualice o lo limpie. Aún hoy, con el uso de sistemas de Inteligencia Artificial (IA), el costo de no entender código escrito por otros (o por la IA) puede ser muy alto si se trata de actualizar o corregir errores importantes en un sistema de cómputo.

Hasta 1970 lo importante era la eficiencia de los lenguajes de programación y menos su comprensión por un operador humano.


La importancia de la legiblidad en el software

Fue desde el año 1987 cuando la industria de la computación consideró que mantener código era más importante que solo cuidar la eficiencia de los cálculos, debido a que construir, actualizar y "limpiar" software son tareas que generan más costos de tiempo y esfuerzo.

Para Sebesta (2012) y Louden, Lambert (2011) de entre las características que debe tener una sintaxis de lenguaje de programación que permita su correcto mantenimiento son:

  • La multiplicidad de características de un lenguaje de programación no debe impedir más de una forma de escribir una instrucción.
  • Debe evitar la sobrecarga de un operador (una palabra clave para ejecutar instrucciones). La sobrecarga será un problema cuando un mismo signo-operador tiene más de un contexto dentro de una sintaxis de lenguaje.
  • Debe tener un correcto equilibrio entre simplicidad y funcionalidad. Si un lenguaje es muy simple (con palabras-clave muy cortas) se corre el riesgo de que quién releea el código se pierda en los términos excesivamente abstractos.
  • Mantener una correcta ortogonalidad en la sintaxis de programación. Eso refiere a "un número menor de construcciones primitivas (básicas) y un conjunto consistente de reglas para combinarlas" (Sebesta, 2012, p. 16). Es decir, establece cuántas combinaciones de una cantidad manejable de sintaxis permiten construir complejidad. O en otras palabras, cuando una sintaxis permite comprender unas pocas reglas para aplicarlo en varias situaciones. Para Louden, Lambert, (2011) un lenguaje es ortogonal cuando "las construcciones del lenguaje no se comportan de manera diferente en diferentes contextos." .
  • Uniformidad: lo que se desea que se haga debe verse en consecuencia. Seguridad: el lenguaje debe tener modos de alerta el errores de tipeo. Extensibilidad: el lenguaje debe permitir agregar nuevos elementos sin incurrir en errores.

Ortogonalidad en sintaxis de programación

Lo ortogonal (cuando dos propiedades en un plano cartesiano en L solo comparten el vértice, pero nunca se intersectan ni se interrumpen) refiere a que la sintaxis del código pueda combinarse de varias formas, sin restricciones complejas o excesivas. Puesto que a más restricciones o condiciones en el uso de una sintaxis de programación, más curva de aprendizaje y más difícil de mantener.

Por ejemplo, a la pregunta de ¿En el lenguaje Javascript es válida una función con argumentos en forma de literales, pero sin valores explícitos? la respuesta es sí; ya que las funciones no necesitan valores en variables, una función puede recibir literales como valores ó valores explícitos en forma de argumentos.

Por lo tanto, el uso de variables y de valores explícitos en funciones de Javascript tienen un comportamiento ortogonal (cualquiera de las dos formas es de uso válido, no dependen de la otra).

En Javascript

// Argumentos en forma de literales
	function suma(a, b) {return a + b;}
	// Recién aquí se les asigna valores a cada uno
	console.log(suma(2, 3));
	// 7

// Argumentos en forma de valores explícitos
	function suma() {return 3 + 4;}
	console.log(suma());
	// 7

Abstracción de procesos

Los lenguajes de programación deben permitir abstracción tanto en procesos como en datos. La abstracción no evidencia cómo, dónde y con qué jerarquías se están trabajando los datos, pero sí permite operar con ellos. La abstracción oculta la complejidad de un proceso y muestra solo el resultado. Por ejemplo, en la ejecución de un algoritmo de clasificación, donde la sola ejecución reordena los elementos sin mostrar el proceso completo, reduciendo la obligación de comprender (y de revisar) todo lo involucrado.

También en la abstracción de sincronizaciones en un sistema (como ocurre con el framework React). Donde la coordinación de estados de elementos no es evidente para el desarrollador; este tan solo debe indicar qué elemento esta cambiando de estado.

Expresividad

Los lenguajes de programación deben permitir expresividad. Esto refiere a que una sintaxis debe contar con una forma más sencilla de indicar una operación. Como en la forma simplificada numero++ para expresar numero = numero + 1.

Confiabilidad

Los lenguajes de programación deben permitir la confiabilidad. Esto debe ocurrir cuando un lenguaje cuenta con eventos que alerten el uso incorrecto de la sintaxis o de las operaciones. Como en la evaluación de tipos de datos, donde se verifica que las operaciones del programa usen tipos de datos correctamente operables. Por ejemplo, un lenguaje no debería compilar variables (transformar código a bits comprensibles por un computador) que no contengan valores (aunque sí podría permitir escribirlo); o al intentar redeclarar valores constantes. Por ejemplo, JS me permite escribir:

En Javascript

const a = 10;
const a = 20; 

Pero al momento de compilarlo dará error (las constantes no se puede redeclarar), por lo tanto el programa no se ejecutará aunque el lenguaje sí me permitió escribir el código.

Dicho lo anterior, los primeros lenguajes de programación eran complejos de comprender y de mantener. Como veremos luego, enfoques como la orientación a objetos fueron creados para agilizar el aprendizaje pero también el pulido, la escala y el mantenimiento del código.

¿Qué es un objeto en lenguajes de programación?

A mi criterio, el término 'objeto' en lenguajes de programación es tres cosas a la vez, en este orden: metáfora, concepto y sintaxis.

  • Metáfora, porque refiere a una cosa única (real o abstracta) con características propias y protegidas, que sustentan un estado o comportamiento autónomo; aunque también le sea permitido compartir datos con otros objetos.
  • Concepto, porque refiere a que ese elemento puede ser categorizado en un conjunto más grande, como una clase.
  • Sintaxis, porque diferentes lenguajes de programación cuentan con sintaxis para representarlo .

Primeras sintaxis de lenguajes de programación

La sintaxis de los primeros códigos máquina eran símiles de expresiones y de notación matemática/algebráicas, como en el lenguaje Short Code, de 1949, para computadores UNIVAC I (Sebesta, 2012, p. 40), el cual un interprete mecánico extremandamente lento ejecutaba las instrucciones. En la siguiente línea, el primer par de número refiere al identificador de las operaciones; luego, 'abs value' da el tipo de valor (uno absoluto); y la expresión final es la operación a ejecutar: la suma de n + 2 elevado a la potencia.

En ShortCode (1949) vs Javascript moderno

// En Short Code para Univac, de 1949
	01 - 06 abs value 1n (n+2)nd power

// En Javascript actual: 
	var x = 2;
	var y = 3;

	// Math.pow -> elevar a potencia indicada...
	// ...El primer valor -> Math.abs(x)
	// ...A la potencia de -> y
	var m = Math.pow(Math.abs(x), y);
	console.log(m);
	// 8

Luego, con el lenguaje ForTran (1952) y sus versiones (I, II, III IV, 77, 90, 95, y luego versiones 2003 y 2008), presentado como el primer lenguaje de conversión de instrucciones a código máquina (a compilado). Los nombres de variables que alojasen números enteros solo podian usar las letras I, J, K, L, M, N. Otros nombres con diferentes letras serían explícitamente de valores decimales.

En ForTran (se agregan comentarios para comprensión)

// ! Punto de entrada del programa; define el bloque principal
	program main

/* ! Obliga a declarar todas las variables explícitamente; evita errores silenciosos por nombres mal escritos */
  implicit none

 /* ! Arreglo de 99 enteros */
  integer, dimension(99) :: Int_List
  integer :: Longitud_lista, Contador, Suma, Promedio, Resultado

  Resultado = 0
  if ((Longitud_lista > 0) .and. (Longitud_lista < 100)) then
	  do Contador = 1, Longitud_lista
		  if (Int_List(Contador) > Promedio) then
			  Resultado = Resultado + 1
		  end if
	  end do
  end if

// ! Finaliza el programa
end program main

Comparación con JS

let resultado = 0;
let longitud_lista = 3;
let int_list = [1,2,3];

for (let i = 0; i < longitud_lista; i++) {
	if (int_list[i] < 5) {
		console.log(resultado++);
	}
}
// 0 1 2

Si bien ForTran permitía crear listados de datos mediante arreglos, no eran listados dinámicos (es decir, un arreglo no podia ser alterado en tiempo real). Solo la versión de Procesamiento de lenguaje en lista Fortrar (1958) permitía ese manejo. Pero este no permitía expresiones condicionales. Recordemos que en la década de 1960 los computadores no contaban con suficiente memoria para que las nuevas versiones se explayen en nuevas características. Si añadían algo, se debía compensar quitando alguna funcionalidad anterior.

Décadas después, en 1958, el estudio de lenguajes de cómputo para simular inteligencia humana representó datos con símbolos, relaciones lógicas, expresiones y cálculo, todo para simular razonamiento. Todo eso debía ser acumulado en listas de datos (o en listas dentro de listas) para representar jerarquías, insertar, buscar o eliminar datos enlistados; o mejor aún, combinar dos o más listas en un complejo enlistado dinámico y actualizable de datos. El lenguaje LISP, creado en 1958 para las primeras investigaciones en Inteligencia Artificial fue creado para ese fin.

Por ejemplo, para enlistar literales que representaban datos anidados en listas. En el ejemplo de abajo: el literal A corresponde a un dato del tipo átomo (un símbolo o un número como dato), (BC) es una sublista, D es otro átomo, y (E) es una lista que aloja una sublista (F G).

Lenguaje LISP (se agregan comentarios para comprensión)

(
	A
	(B C) 
		D 
		(E 
		(F G)
		)
)

// Y en otros casos, para definir una función
	// DEFUN        -> define un dato tipo función
	// equal_lists  -> nombra la función
	// lis1, lis2   -> nombres de argumentos 
	(DEFUN nombres_lists (lis1 lis2)

En esos años también se usó el lenguaje Algol (ALGOrithmic Language, 1958), creado por un comité de científicos alemanes y suizos para contrarestar la influencia de la empresa estadounidense IBM. Para mejorar la legibilidad, Algol introdujo bloques de código indicados con una palabra de inicio y otra de final del código. Antes de eso las sintaxis de programación no estaban agrupadas en secciones, sino más bien eran una lista interminable de instrucciones. También permitía indicar claramente desde dónde inicia un listado y dónde finaliza. Finalmente, se inserta un operador de asignación := para diferenciarlo de la igualdad '='.

Lenguaje Algol

begin
	integer array listadoNombres[1:99];
	integer sum;
	sum := 0
end

En resumen, en las sintaxis de muchos los lenguajes de programación antes y durante la década de 1960 bastaba con imitar los símbolos del álgebra y las matemáticas, o usar una cantidad muy limitada de literales para alojar valores y generar resultados. Pero esos atajos convertían un extenso código en algo inmanejable y con la obligación de invertir demasiado tiempo para escalarlo o corregirlo.

Debido a la necesidad de colaboración entre profesionales en pos de un constante mejoramiento del código (técnicamente conocido como depuración o debugging) fue necesario un nuevo modelo que permita crear y simular comportamientos más dinámicos, aleatorios y de fácil actualización de los estados (y comportamientos) de los datos contenidos en un sistema de cómputo.

Siguiente artículo: los paradigmas de programación acogen un moderno enfoque.


Programación orientada a objetos, una revisión histórica y técnica


LIBROS CONSULTADOS
Sebesta, R., 2012. Concepts Of Programming Languages. Tenth Edition, Pearson.

Scott, Michael, 2006. Programming Language Pragmatics. MK editores. Louden, Lambert, 2011. Programming Languages, Principles and Practice. Third Edition.