¿Cuáles son algunos ejemplos de la Décima Regla de Greenspun en acción?

La forma en que lo experimenté fue con una plataforma de aplicación de entrada de datos basada en datos que se desarrolló internamente en la empresa donde trabajé a mediados de la década de 1990. Todas las piezas de ejecución fueron escritas en C. Se utilizó una base de datos relacional de una manera no relacional. Se almacenó una estructura de árbol para las rutas de ejecución de una aplicación, elementos de datos para pantallas y respuestas del usuario a cada pantalla. La estructura de árbol contenía declaraciones condicionales que serían recogidas por la plataforma de aplicación, que se utilizarían para probar las respuestas de los usuarios, y luego para dirigir la plataforma de aplicación en qué dirección ir en el árbol, que lo dirigiría al siguiente conjunto de elementos de datos que se mostrarán para la siguiente pantalla.

Cuando se creó la idea de XML, recuerdo que nuestro vicepresidente de ingeniería dijo que la estructura de la base de datos que habían creado era muy similar.

La forma en que era “la mitad de Common Lisp” era que estaba basada en datos. Toda la aplicación estaba en una estructura (una base de datos). Tenía elementos de datos que en cierto sentido fueron “evaluados” (yo lo llamaría “interpretados”), que determinaron lo que se mostraba en la pantalla y la ruta de ejecución (cuando se combinaba con la entrada del usuario). Las respuestas de los usuarios también se almacenaron en la misma estructura y se vincularon a los elementos de datos para cada pantalla. Esto era, en cierto sentido, “código auto modificable”, ya que conceptualmente los datos del usuario se almacenaban con el código de la aplicación.

La razón por la que era “la mitad de Common Lisp” era que no tenía funciones como objetos de primera clase, y no tenía una evaluación meta-circular. No fue autorreferencial. La aplicación no tenía forma de autogenerar nuevas pantallas, o de auto modificar las existentes (excepto para almacenar datos en ellas a partir de las respuestas de los usuarios), o para crear nuevas rutas en el árbol. Tampoco había una forma programática de agregar nuevas características de datos y nuevos tipos condicionales al árbol que hubiera agregado nuevas capacidades a aplicaciones individuales. Esto fue algo que años después deseé habernos dado cuenta. Hubiera hecho que la plataforma de aplicaciones sea mucho más poderosa, como lo fue. En cambio, cada vez que queríamos agregar nuevas pantallas, o modificar el árbol de ejecución, modificamos un conjunto separado de tablas relacionales, que contenían los datos de la pantalla, y modificamos un programa C diseñado de forma poco elegante, que tenía todos los lógica del árbol de aplicaciones codificada en él. Se generó el árbol. Tratar con eso fue una tarea real, porque el generador de árboles se hizo tan grande que desarrolló errores propios. Lo modificamos con bastante frecuencia.

Además, necesitábamos poder generar informes a partir de los datos que se ingresaron, y debíamos poder tomar partes seleccionadas de los datos del árbol y exportarlos a datos relacionales. Lo principal en lo que trabajé para este proyecto fue un intérprete de lenguaje que permitiera a un diseñador de informes seleccionar partes de los datos del árbol y formatearlo para un informe impreso, o una persona de TI para seleccionar partes de los datos del árbol exportar a tablas relacionales. Una pregunta natural podría ser: “¿Por qué no tenía un lenguaje para modificar el árbol en primer lugar?” Buena pregunta. El proyecto fue diseñado de tal manera que las piezas de lenguaje eran solo para uso del cliente. Creo que fue una elección desafortunada. Hicimos muchas cosas en las que un lenguaje para la plataforma en sí habría sido muy útil.

Si hubiéramos podido crear un entorno semántico para tratar con la base de datos del árbol directamente, en sus términos (no en términos relacionales), creo que nos habría creado menos dolores de cabeza. El intérprete en el que trabajé (para generar informes y exportar datos) podría haber sido mucho más fácil de escribir.

Puede que no sea ni aquí ni allá, porque la compañía para la que trabajé básicamente desarrolló esta plataforma de aplicación para un cliente. Si les hubiera gustado, lo habríamos usado para otros clientes, pero terminaron odiándolo. No funcionaba bien con su flujo de trabajo, por lo que lo abandonamos por un enfoque de aplicación convencional basado en código, en una plataforma de aplicación convencional, que les gustó más. Aun así, años después, utilicé la plataforma de aplicaciones que describí anteriormente como inspiración para pensar cómo podría haberse hecho mejor. Fue una experiencia valiosa para mí, al menos.

La idea del lenguaje incrustado (como muestra David) es parte de ella, sí. Tan pronto como desee algunas posibilidades de personalización en tiempo de ejecución, necesita estas cosas. ¿Y qué sería mejor que tener la capacidad de programar directamente en el mismo lenguaje y entorno exactos en los que se escribió el programa base (y se ejecuta)? Que casi todos los Lisps (incluido CL) le brindan de forma gratuita, pero casi todos los demás lenguajes (especialmente la multitud C) hacen que sea extremadamente difícil o casi imposible (es decir, volver a implementar un lenguaje completamente nuevo dentro de su programa solo para este propósito).

Otra parte detrás de esto es que tan pronto como comience a implementar una “biblioteca” interna de funciones y estructuras de datos, está rehaciendo lo que Lisp (y especialmente CL) ya puso a disposición.

P.ej

  • ¿Creando su propia estructura de árbol usando estructuras y punteros? He estado allí de manera extremadamente óptima, bien probado, etc. en CL.
  • Hacer una pseudo idea de OOP donde use punteros de función dentro de estructuras: ¿por qué no simplemente ir con una OOP implementada correctamente … oh! Lo sé … ¡no te gusta la idea de CLOS!
  • ¿Quieres una tabla hash? ¿Rehacer todas sus funciones de hash y construir sus propias matrices de crecimiento dinámico? ¿Qué sucede contigo? Oh, tienes demasiado tiempo libre … y crees que todo lo que hagas siempre será mucho mejor de lo que ya lo han hecho los expertos.
  • ¿Desea trabajar con cadenas UTF8 en lugar de ANSI? Nuevamente, hasta el último bit que tendría que hacer manualmente en C, CL lo tiene cubierto, mucho mejor de lo que se haría (especialmente si no tiene años para hacer que el suyo funcione tan bien como el de CL).
  • ¿Quieres un número de precisión arbitrario? ¿Desea que funcione de manera correcta, rápida y fácil con tipos de números primitivos? Buena suerte con eso. Simplemente use la numeración integrada de CL, que usa automáticamente el tipo requerido según el tamaño del valor; ni siquiera necesita saber que se convirtió de int a bigint y viceversa.

Todos estos son pequeñas muestras de cosas de alto nivel que cada programador de C tiende a escribir … generalmente específicas para cada programa. La idea suele ser que lo están haciendo “específico” para un programa y, por lo tanto, exactamente diseñado para la necesidad … solo para descubrir que siguen haciendo exactamente el mismo algoritmo cada vez. Usted encuentra a algunos que luego comparten, como una biblioteca de terceros, pero rara vez está muy bien diseñada al principio; solo una vez que dicha biblioteca se ha utilizado mucho y se ha revisado mucho, comienza a llegar a “diseñado desde el principio hasta ser lo mejor posible ”. Bibliotecas de sobrealcance similares a CL.

En este sentido, la “décima regla” es un hermano del axioma “no reinventar ruedas”.

Todo lo que incorpora LUA como ejemplo, o todos los navegadores del mundo con Javascript. En general, cualquier cosa con un lenguaje de extensión, incluidos los que odian a los emacs que trabajan con VIM.