Si pudiera exportar una característica de Go a otros idiomas, serían las interfaces. - Russ Cox

Mi
compilador Pascal extremadamente simple ya se ha convertido en el tema de
dos publicaciones sobre Habré. Desde su escritura, el lenguaje ha adquirido todas las herramientas faltantes requeridas por Pascal estándar, y muchas de las cosas que Borland agregó a Pascal en su época dorada. El compilador también aprendió algunas de las optimizaciones locales más simples, suficientes solo para evitar que sus ojos sangren cuando mira la lista del desensamblador.
Sin embargo, la jungla de la programación orientada a objetos permaneció completamente intacta. Entonces, ¿por qué no servir como campo de pruebas para que el compilador experimente en esta área? ¿Y por qué no nos inspiramos en las palabras de Russ Cox, hechas en el epígrafe? Intentemos implementar métodos e interfaces Go-style en Pascal. La idea es interesante solo porque todos los compiladores populares de Pascal en el pasado (Delphi, Free Pascal) esencialmente tomaron prestado el modelo de objetos de C ++. Es interesante ver cómo un enfoque completamente diferente, tomado de Go, se arraiga en el mismo terreno. Si estás listo para seguirme con bastante ironía, dejar la pregunta "¿Por qué?" Y tomar lo que está pasando como un juego, bienvenido a cat.
Principios
Por "estilo Go" entenderemos varios principios en base a los cuales implementaremos métodos e interfaces en Pascal:
- No hay conceptos independientes de clase, objeto, herencia.
- El método puede implementarse para cualquier tipo de datos en particular. No necesita cambiar la declaración del tipo en sí.
- Una interfaz es compatible con cualquier tipo de datos en particular para el que se implementan todos los métodos enumerados en la declaración de interfaz. Una declaración de un tipo de datos particular no requiere que implemente una interfaz.
Implementación
Para declarar métodos e interfaces, las palabras clave estándar de Pascal para e interfaz se utilizan en el nuevo rol. No se ingresan nuevas palabras clave. La palabra
for
se usa para indicar el nombre y el tipo del destinatario del método (en la terminología Ir). Aquí hay una descripción del método de ejemplo para un tipo
TCat
declarado
TCat
con un campo
Name
:
procedure Greet for c: TCat (const HumanName: string); begin WriteLn('Meow, ' + HumanName + '! I am ' + c.Name); end;
El receptor es en realidad el primer argumento del método.
Una interfaz es una entrada Pascal normal, en cuya declaración el
record
palabras se reemplaza por la
interface
palabras. En este registro, no está permitido declarar ningún campo, excepto los campos de tipo procesal. Además, se agrega un campo
Self
oculto al comienzo de la grabación. Almacena un puntero a datos de ese tipo en particular, que se convierte en un tipo de interfaz. Aquí hay una declaración de interfaz de ejemplo:
type IPet = interface Greet: procedure (const HumanName: string); end;
Al convertir un tipo específico a una interfaz, el compilador verifica la presencia de todos los métodos requeridos por la interfaz y la coincidencia de sus firmas. Luego establece el puntero
Self
, llena todos los campos de procedimiento de la interfaz con punteros a métodos de un tipo particular.
En comparación con Go, la implementación actual de interfaces en Pascal tiene limitaciones: no es posible consultar dinámicamente un tipo de datos específico que se haya convertido a un tipo de interfaz. En consecuencia, las interfaces vacías no tienen sentido. Quizás el próximo paso de desarrollo será llenar este vacío. Sin embargo, incluso en su forma actual, las interfaces proporcionan polimorfismo, útil en muchas tareas no tan triviales. Consideraremos uno de esos problemas.
Ejemplo
Un buen ejemplo del uso de interfaces es un
programa para renderizar escenas tridimensionales utilizando el método de trazado de rayos. La escena consta de cuerpos geométricos simples: paralelepípedos, esferas, etc. Cada rayo emitido por el ojo del observador debe ser rastreado (a través de todos sus reflejos) hasta que llegue a la fuente de luz o llegue al infinito. Para hacer esto, se asigna un método de
Intersect
a cada tipo de cuerpo, que calcula las coordenadas del punto donde el rayo golpea la superficie del cuerpo y los componentes normales en este punto. La implementación de este método para diferentes tipos de cuerpos es diferente. En consecuencia, la información sobre los cuerpos se almacena convenientemente en una matriz de entradas de interfaz
Body
, y para todos los elementos de la matriz, se llama a su vez el método
Intersect
. La interfaz redirige esta llamada a un método específico según el tipo de cuerpo.
Esto puede parecer una escena construida de la manera descrita:

El código fuente completo del programa, incluida la descripción de la escena, toma 367 líneas.
Resumen
La implementación más simple del polimorfismo en el propio compilador de Pascal resultó ser fácil, dando los primeros frutos rápidamente. Se pueden esperar algunas complicaciones en la tarea de definir dinámicamente un tipo de datos específico, que se convirtió en un tipo de interfaz. Los esfuerzos también requerirán la eliminación de conflictos no obvios con los mecanismos de verificación de tipo estándar de Pascal estándar. Finalmente, además de todas las preocupaciones sobre las interfaces, una lucha desigual continúa con Microsoft en torno a las falsas alarmas de su Windows Defender al lanzar algunos ejemplos compilados.