Programación automática: escribir código que escribe código [cerrado]


105

Después de leer el libro El programador pragmático , uno de los argumentos que encontré más interesantes fue "escribir código que escribe código".

Intenté buscar en la red algunas explicaciones o artículos más al respecto, y aunque encontré algunos buenos artículos sobre el tema, todavía no he encontrado ninguna implementación de código específica o buenos ejemplos.

Siento que todavía no es un argumento tan común, algo que carece de documentación o que no es aceptado por tanta gente, y me gustaría saber más al respecto.

¿Qué opinas sobre el tema? ¿Es algo que realmente aumentará su productividad? ¿Cuáles son algunos buenos recursos sobre el tema, entre libros, blogs, presentaciones de diapositivas, etc.?


Algunos ejemplos de código serían muy apreciados para permitirme comprender mejor su implementación.


Aquí está la página wiki sobre el tema con varias técnicas de programación relevantes, como Meta Programming, Generative Programming y Code Generation.


32
Una vez escribí el código que escribió el código que escribió el código ... :)
Benjol

99
@Benjol: ¿Estabas escribiendo en Lisp?
compman

11
Además, los lenguajes del lado del servidor hacen esto todo el tiempo generando HTML, CSS y JavaScript. Podría tener una secuencia de comandos del lado del servidor que cree una secuencia de comandos del lado del servidor que cree html con javascript que cree más html, y nadie se preocupará por lo común que es.
zzzzBov

8
Si aún no lo ha hecho, consulte esta serie de artículos de IBM developerWorks: " El arte de la metaprogramación " Parte 1 , Parte 2 y Parte 3 .
John Tobler

3
AtomWeaver ( atomweaver.com ) es un buen ejemplo de programación automática: en primer lugar, crea miniprogramas reutilizables en Lua. Luego, modela su sistema reutilizando estos activos. AtomWeaver luego teje un programa Lua que contiene sus "mini-generadores" para generar el código fuente final del sistema. Luego puede ajustar su modelo y volver a generar.
Rui Curado

Respuestas:


49

En el mundo de Lisp, es bastante común ver el código que escribe el código que escribe el código (y así sucesivamente). Por lo tanto, cualquier proyecto Lisp o Scheme de tamaño decente servirá como un buen ejemplo de código. Recomiendo mirar el compilador Racket y las fuentes de tiempo de ejecución, así como Bigloo , sus bibliotecas son simplemente geniales.

En cuanto a la productividad: estoy usando la metaprogramación como una técnica dominante en casi todo mi trabajo de desarrollo, y claramente ayuda mucho, reduciendo el tamaño del código y aumentando su legibilidad. La clave está en usar lenguajes específicos de dominio , y la metaprogramación es una de las formas más eficientes de implementarlos.


67

Prefiero ir un poco más lejos y, en lugar de escribir código que escriba código, escribir código que genere objetos, métodos, funciones. Esto se puede lograr con macros Lisp o capacidades de modificación dinámica del programa Ruby, por ejemplo.

La pequeña diferencia es que no termina con los archivos fuente que se generaron automáticamente. Por lo general, estos archivos no son legibles por humanos y no se pueden modificar, entonces, ¿por qué molestarse con ellos? No me gusta la idea de aumentar mi base de código con algo que no puedo controlar.

Un libro que disfruté leyendo sobre el tema fue Metaprogramming Ruby (si sabes el lenguaje Ruby)


Edite después de la siguiente pregunta en el comentario:

¿Por qué debería ser útil si todavía tengo que codificar el código generador? ¿Debo escribir un código capaz de generar diferentes cosas dependiendo de la entrada del usuario, para poder reutilizarlo una y otra vez?

Primero, la metaprogramación no es un objetivo, sino una herramienta. No use la metaprogramación porque "es genial" o "X dijo que todo desarrollador debería usarla".

Creo que una buena razón para usar la metaprogramación es generalizar algún patrón común (patrón como algo que se repite) que has encontrado en tu código y que ninguna otra técnica de programación habitual (herencia, patrones de diseño, etc.) puede lograr.

Como dijo Jordan , un caso de uso típico es el manejo de bases de datos y ORM (asignación de relación de objetos). Una vez más, en Ruby, debe mirar ActiveRecord, que es un gran ejemplo de metaprogramación aplicada a ORM.

Como nota final:

No piense "Quiero aplicar metaprogramación, ¿dónde podría aplicarlo en mi código?".

Piense "Veo este patrón que se repite en todo mi código, no puedo encontrar una manera de refactorizar el código en algo más pequeño y más reutilizable. ¿ Quizás la metaprogramación puede ayudarme?"


3
@Jose: más comúnmente generas código a través de plantillas. Hay una velocidad apache (N-), por ejemplo, o las plantillas T4 de Visual Studio. Luego, solo tiene un programa que alimenta los metadatos en sus plantillas y crea nuevos archivos a partir de entonces. Es bastante fácil y lo hago todo el tiempo para generar esqueletos de interfaz de usuario, entidades, etc.
Falcon

2
@Jose Faeti, eche un vistazo más de cerca a las macros de Lisp (o Clojure o Nemerle, según las preferencias de su plataforma).
SK-logic

1
Agregaría que la metaprogramación puede reemplazar algunos patrones como la política o el estado, pero sin costo de tiempo de ejecución. Esto no es solo para problemas que no se pueden lograr con una refactorización común, sino que también es una mejor alternativa.
deadalnix

1
@Jose Faeti: Veo que sabes algo de Python. También tiene capacidades de metaprogramación, aunque realmente no las he usado. Eche un vistazo a Dangerously Advanced Python PDF
Kit

3
@Falcon: IMO que es la peor forma de generar código; es una solución muy pobre para idiomas sin una función de metaprogramación incorporada. En lugar de generar Java o C #, sería mejor escribir ese código en un lenguaje JVM o .NET de nivel superior.
Kevin Cline

19

Aún mejor, use el código que alguien más escribió que escribe su código por usted.

La automatización del código es generalmente buena para ORM y otros códigos de interacción de bases de datos, y por supuesto para la creación de códigos repetitivos pero similares.

Por supuesto, si está creando muchas clases de aspecto similar, tal vez podría haber logrado lo mismo en un lenguaje dinámico mucho antes, pero estoy divagando.

Muchas personas lo aceptan, aunque a menudo encontrarás el software etiquetado como generador de código.

Vea empresas y productos como CodeSmith y MyGeneration, o eche un vistazo a este artículo de Wikipedia: http://en.wikipedia.org/wiki/Comparison_of_code_generation_tools


66
No hay nada mejor. Su pequeño y precioso código no puede ser administrado adecuadamente por la herramienta de generación de código de otro tipo, ya que ese otro tipo no sabe nada sobre sus detalles. El uso más productivo de la metaprogramación es implementar lenguajes específicos de dominio, y, como su nombre lo indica, son específicos de su dominio problemático, nadie más que usted puede implementarlos.
SK-logic

@ SK-logic: ¿qué pasa con el código generado por ORM? Es generado por otra herramienta / biblioteca y aún cumple con muchas necesidades de proyectos.
David

@David, para ser honesto, no estoy muy convencido con los ORM genéricos. Tuve tantos problemas con ellos en el pasado, a menudo recurrí a implementar mis pequeños ORM específicos.
SK-logic

1
@ Jordania, todas esas herramientas son demasiado específicas (y peor , basadas en texto , es decir, inferiores por diseño). Estoy hablando de la metaprogramación adecuada en su lugar.
SK-logic

1
@AtillaOzgur, pueden ser "muy buenos", cierto. Pero no son mejores que los eDSL. Obviamente, la generación de código independiente es mucho más limitada y mucho menos flexible que la macroprogramación.
SK-logic

16

Uno de los ejemplos clásicos es lex y yacc. Su propósito principal es evitar el trabajo pesado de escribir cualquier tipo de analizador. En el camino, hacen que sea mucho más rápido construir analizadores complejos con muchas reglas y estados, y también evitan todos los errores sorpresa cometidos por las personas que usan los suyos.

Esta es también la idea detrás de c, que es una herramienta para escribir ensamblador. Lo mismo ocurre con cualquier idioma de alto nivel que le interese nombrar. Para las herramientas que escriben código para usted, existen algunos paradigmas simples.

Un IDE adecuado ayuda al proporcionar documentación a su alcance, finalización automática inteligente y fragmentos de código. Los IDE también incluyen varias plantillas, por lo que no tiene que iniciar un programa desde cero. Hay programas para tomar un diagrama uml y desglosar las clases en un lenguaje de alto nivel.

Finalmente, puede escribir sus propias herramientas para la generación de código dentro de su conjunto de problemas. Así es como lex y yacc comenzaron por primera vez. Cualquier tipo de lenguaje específico de dominio existe precisamente por esta razón. Usted crea algunos bloques de construcción que describen su solución en un código más fácil de entender, resumiendo actividades comunes o secciones complicadas con comandos simples. No está buscando una solución para cada problema, solo una definición más fácil del problema específico con el que está tratando.

En cierto sentido, todo lo que haces por encima de la capa binaria es la automatización del código.


Esa es una muy buena vista. En general, es solo otro de los muchos métodos que los programadores intentan usar para facilitar sus operaciones y enfocarse en un mayor nivel de codificación, en lugar de los detalles del código de sintaxis.
Jose Faeti

1
@Jose Faeti El artículo de wikipedia en.wikipedia.org/wiki/Automatic_programming tiene enlaces a varias herramientas diferentes, si está interesado en obtener más detalles. También sugiero leer sobre lex y yacc, ya que hay bastante más documentación y descripción para ellos.
Spencer Rathbun

En lenguajes suficientemente potentes (por ejemplo, C ++ en lugar de C), las herramientas externas como lex y yacc son innecesarias.
Kevin Cline

YACC no escribe "ningún tipo de analizador". Escribe un tipo específico de analizador (LALR) que es muy difícil de hacer sin la ayuda automatizada. Hay otro tipo de analizador (descenso recursivo) que es mucho más fácil de escribir y acertar, y en consecuencia más fácil de leer y comprender lo que está sucediendo.
Mason Wheeler

@MasonWheeler El tipo de analizador se refería a las gramáticas que se pueden crear para resolver problemas, en un sentido amplio y no exacto. Al leerlo un año después, no está tan claro como me hubiera gustado. Sin embargo, no estoy seguro de estar de acuerdo con usted en que los analizadores LL (*) sean más fáciles de escribir y usar.
Spencer Rathbun

13

Metaprogramación

La metaprogramación es una técnica controvertida en muchas tiendas. La razón es, como cualquier herramienta poderosa, la magnitud de la ayuda o el daño es grande.

Pros

  • Más expresivo, menos código para escribir y mantener (a menudo por un orden de magnitud o más)
  • Consistencia, comportamiento más consistente sobre la clase de problemas que está resolviendo con el código
  • Productividad, menos código para una solución a un espacio de problemas mayor

Contras

  • Complejidad, puede ser muy complicado aunque haya menos código
  • Seguridad, a veces se sacrificará la seguridad tipo y el análisis estático en general
  • Los errores afectan más, los pequeños errores tendrán un mayor impacto

Soy un gran admirador de la metaprogramación, pero lo he estado haciendo durante mucho tiempo. Para mí, la compensación de un tamaño de código reducido y un comportamiento consistente más que compensar los riesgos. Menos código significa menos errores, menos código para mantener, y generalmente puedo agregar grandes piezas de funcionalidad muy rápidamente.

Sin embargo, esto no significa que creo que todos los programadores deberían participar. He visto y tuve que solucionar grandes problemas creados por la metaprogramación. Por lo general, cuando las personas que no entienden el concepto y han intentado ampliar la funcionalidad, o simplemente corregir un error. Se necesita una mentalidad particular que, como mínimo, esté orientada al detalle. La pregunta para usar técnicas de metaprogramación debe ser una decisión del equipo . Si tiene miembros del equipo que no entienden, no tienen el temperamento para ello, o simplemente están en contra, ninguno de los equipos debe usar la metaprogramación.


Gracias por las consideraciones útiles! ¿Podría sugerirme una tarea realmente simple y básica que pueda implementar usando la metaprogramación, lo que me ahorrará tiempo con respecto a la codificación normal, un pequeño ejemplo de código?
Jose Faeti

jaja me recuerda un error que tuve hace varios años con GCC. 162 líneas para poner el mensaje de error en mi pantalla. Metaprogramación recursiva FTW!
deadalnix

66
La complejidad de la metaprogramación está muy sobrevalorada. No hay absolutamente nada complicado, siempre y cuando esté utilizando las herramientas adecuadas. Y los DSL son mucho más fáciles de depurar y mantener que el código típico repetitivo. Además, no puedo entender por qué uno debería sacrificar la seguridad de los tipos: es exactamente lo contrario, las DSL también pueden tener sistemas de tipos específicos de dominio y altamente eficientes.
SK-logic

2
@ SK-logic: no todos los idiomas admiten bien la metaprogramación. Entonces, a veces se sacrifican cosas como la seguridad de tipos (es decir, C) . Además, la metaprogramación no es solo DSL. Incluye cosas como programación de estilo de despacho, genéricos, currículum, inspección de objetos, aplicación dinámica, etc. En cuanto a la complejidad, creo que es fácil para nosotros (personas con experiencia en metaprogramación) decir que no es complicado. He visto otra lucha para comprender todos los casos en que se ejecutará el código. Depende principalmente de su experiencia y la técnica involucrada.
dietbuddha

@dietbuddha, ¿podría explicarnos por qué sacrificar la seguridad de su propio DSL, sin importar cómo se implemente? Puede escribir un intérprete ad hoc en una C pura con un sistema de tipo fuerte (vea Abrazos, por ejemplo). Puede escribir un generador de código dirigido a C que realice todas las comprobaciones de tipo en sí, sin depender del sistema de tipos de idioma de destino. Para la complejidad: la mayoría de las personas lo hacen de una manera innecesariamente compleja, mientras que las mismas metodologías de diseño se pueden aplicar a la generación de código como en la programación "normal". Casi no se requieren nuevos conocimientos.
SK-logic

9

La mayoría del código escribe código. Por ejemplo, el código php ayuda a escribir html. La biblioteca php pdo ayuda a escribir llamadas SQL. Las funciones de E / S de archivo escriben código para comunicarse con el sistema operativo. Incluso una llamada de función regular es una referencia a otro bloque de código que se ejecuta. Entonces sus llamadas a funciones están escribiendo código.

En términos generales, podemos pensar en la informática como códigos de escritura que escriben códigos recursivamente formando una pila que termina cuando se encuentra con la realidad física de los códigos conectados al hardware.


3
No llamaría html a un lenguaje de programación. Es una sintaxis para documentos
Simon Bergot

3
@ Simon es un punto interesante. Hay toda una variedad de poderes expresivos para los diferentes códigos que usamos. El código puede escribir en un idioma más débil, un idioma más fuerte o en su propio idioma.
Ben Haley

5

La forma en que hace esto varía según sus requisitos. Suponiendo que está utilizando la generación de código estático, podría escribir toda la infraestructura usted mismo, o podría usar un generador existente como CodeSmith o MyGeneration. Usando estos solo necesita escribir las plantillas requeridas.

Mi último proyecto relacionado con esto fue algunas pantallas básicas de ASP.NET CRUD (la generación de código es buena para esto). El proceso fue definir entidades como metadatos en archivos xml. Escriba plantillas para cubrir los diversos artefactos requeridos (clases de entidad, repositorios, clases de servicio, controles asp.net, páginas asp.net, etc.). Ejecute el proceso de generación y aplique estilo a la salida.

Hay algo de sobrecarga al escribir las plantillas, pero se pueden reutilizar para proyectos similares posteriores. De manera similar, los cambios en los datos subyacentes se manejan cambiando los metadatos y volviendo a ejecutar la generación, haciendo que los cambios sean más simples y rápidos de implementar.

En cuanto a las pruebas. Dado que este es un sistema con plantilla, necesitará pasar un tiempo validando inicialmente la salida del proceso, si su plantilla es incorrecta, toda la salida de esa plantilla será igualmente incorrecta. Una vez que esté satisfecho con esto, también puede usar los generadores de código para crear pruebas básicas a partir de los metadatos xml que luego puede extender para cubrir casos especiales. Sin embargo, recuerde que aún puede necesitar realizar pruebas de código manuales para atender cosas específicas, la generación de código reduce su trabajo, no lo elimina por completo.


5

En nuestra empresa utilizamos algunas herramientas que realmente generan clases de C ++ o C # con datos descargados de Internet. Estas clases son contenedores de datos y contienen una gran cantidad de objetos en las listas.


¿Algo parecido a los fragmentos de código que se encuentran en algunos IDE como Visual Studio, por ejemplo?
Jose Faeti

@Jose Nuestra herramienta es solo una aplicación para convertir la salida HTML a una clase. Entonces, en lugar de descargar los datos cada vez que se inicia la aplicación, los descargamos una vez y hacemos una clase.
Holli

5

La metaprogramación ha sido parte de la programación durante mucho tiempo. Considere no solo herramientas como SWIG o diseñadores WYSIWYG, que crean código, sino también herramientas en lenguaje como el preprocesador de C, o incluso las plantillas de C ++ y los genéricos de C # / Java, sin mencionar Reflection.

De hecho, podría argumentar que cada compilador es solo otro metaprograma: toman el texto del programa y generan la máquina o el código VM. ¿Y la vida sin compiladores? Búho


Así es, pero ¿cómo puede implementarlo en su propio lenguaje de programación para aumentar su productividad? Eso es lo que me estoy perdiendo.
Jose Faeti

5

Aquí hay un ejemplo concreto de mi pasado.

Estaba trabajando en un sitio que tenía alrededor de 50 MB de código fuente de Delphi usando el BDE para acceder a los datos. Querían cambiar a usar el Acceso directo a Oracle para permitir una actualización de Oracle más allá de la versión más alta compatible con BDE (8i si recuerdo correctamente).

Entonces, en lugar de hacer que un equipo de codificadores trabaje en cada formulario y módulo de datos cambiando cada componente manualmente, escribí un script PERL que:

  1. Analizó el DFM (archivo de formulario) e identificó todos los objetos TQuery, TTable, TStoredProcedure y TDatabase, almacenando los elementos en una lista.

  2. Analicé el PAS (código) e identifiqué el uso de los objetos. ¿Las actualizaciones de TQueries se hicieron o seleccionaron? Además, identificó cualquier objeto creado en código en lugar de soltarlo en un formulario en el IDE.

  3. Reescribió el DFM y PAS cambiando los tipos de objeto adecuadamente (por ejemplo, TTable -> TOracleDataSet con la propiedad SQL establecida en "select * from", etc.) y las llamadas al método. Además, se agregaron llamadas de método adicionales si fuera apropiado para cerrar, abrir y establecer parámetros.

En resumen, 3 semanas de trabajo ajustando el script para trabajar en diferentes aplicaciones escritas por diferentes equipos con diferentes estilos de codificación en lugar de la estimación original de más de 5 desarrolladores que trabajan durante 6 meses.

Y la razón por la que incluso pensé en usar ese enfoque fue leer El Programador Pragmático


Eso es genial, ahora estoy en Perl desde hace un par de días y ya hice algunas herramientas de productividad para generar espacios de trabajo básicos para el desarrollo web, con todos los directorios, archivos, etc., simplemente escribiendo "crear espacio de trabajo". :)
Jose Faeti

1
@Jose Esa es la idea. Use lenguajes de secuencias de comandos para automatizar cosas repetitivas. Puede ser por una sola vez donde obtienes un aumento de productividad de 8x o, como en tu caso, algo que consume mucho tiempo y que harás una y otra vez.
mcottle

4

Pides ejemplos ...

Al trabajar con SQL, no debe cambiar la base de datos directamente, sino que se supone que debe ejecutar scripts que realicen los cambios que desee, incluidos los cambios estructurales en la base de datos (agregando tablas, columnas, claves primarias, restricciones, etc.) . Con mucha frecuencia, tendrá que realizar la misma acción contra muchas tablas o columnas al mismo tiempo, y hacerlo una por una sería tedioso, un script corto que genera un script más grande que hace lo que quiere puede ser real ahorrador de tiempo.

Por ejemplo, antes de que se introdujera el tipo de datos DATE en MS SQl Server, la única opción para una columna de fecha era DATETIME, que tiene una parte de tiempo, una parte de tiempo que dificulta un poco el manejo de los datos. Al actualizar a una versión con el tipo de datos Fecha, es posible que desee actualizar las columnas donde la hora siempre es 00:00. En una base de datos con docenas o incluso cientos de columnas DateTime, esto llevaría bastante tiempo. Pero es fácil escribir un script que consulte todas las tablas, verificando cada columna con un tipo de datos DATETIME para ver si es el momento diferente de 00:00 y, si no, crea una declaración ALTER para que la tabla / columna cambie el tipo de datos a DATE. Presto, código que escribe código.


3

Eche un vistazo a las macros CL (labios comunes). En mi opinión, eso es exactamente lo que quieres. Labios es perfecto en metaprogramación.

También sugiero Nemerle si desea tener poderes .NET con soporte perfecto para metaprogramación (incluidas macros)

Pero si desea un verdadero motor de generación de código, eche un vistazo a la economía de Apache


3

Solo estoy trabajando en tal herramienta. En nuestro caso particular, generamos el código VB.NET basado en la capa de datos en las firmas de las funciones en la base de datos.

Comenzar a trabajar en y con la generación de código es difícil al principio ya que no tiene idea de cómo se debe generar el código, pero una vez que tiene un conjunto establecido de reglas, y el código que debe generarse siempre se puede generar en función de esas reglas , trabajar con ese código no es tan difícil. Por supuesto, dependiendo de la complejidad de la generación del código y del número de reglas, la tarea puede volverse más difícil. Pero, en esencia, la generación de código automático se utiliza para tareas de codificación repetitivas y no para código avanzado que varía mucho.

Probar la salida es doble. Primero debes asegurarte de que el código se compila, y eso es fácil. Luego, debe asegurarse de que la salida haga lo que quería que hiciera en función de los parámetros en los que se generó ... y la dificultad de eso varía en la complejidad del código que genera.

Mi recomendación sincera es que si sientes que escribes código de forma repetitiva y puedes permitirte el tiempo ... Intenta pensar si lo que estás haciendo no se puede hacer con el código generado. Y si es así (si es un código repetitivo que casi siempre es el caso) piense cuántas veces tendrá que extender, modifique ligeramente ese código y también cuántas veces tiene que escribir ese tipo exacto de código. Si la respuesta a cualquiera de estos es "muchos", entonces debería considerar seriamente hacer un generador para ese código .

Espero que ayude,
IPP


¡Gracias por responder! ¿Cómo se implementan realmente las reglas en su ejemplo?
Jose Faeti

1
No puedo decirte todas las reglas, pero puedo darte algunos ejemplos. Analizamos la interfaz expuesta por una base de datos Oracle y tomamos en consideración las firmas de las funciones en la interfaz Oracle. En función de la firma, generamos el nombre de la función de capa de datos. sabemos que siempre obtenemos de db una tabla de datos de Oracle como resultado, que analizamos y guardamos en una matriz de tipos de objetos especiales que usamos para almacenar nuestros datos. también, en función de los parámetros de entrada / salida de la firma de la función db, agregamos los parámetros de entrada y salida correspondientes a las funciones que generamos, etc.
Ioan Paul Pirau

3

Tengo un módulo PHP que genera una página web que contiene código JavaScript que genera HTML. Eso es tres capas allí mismo. ¡Boy era tan difícil de leer!

En una clase de programación, teníamos que escribir un programa que tomara una cadena de fórmula del usuario y la analizara y mostrara el valor. El solucionador más impresionante simplemente tomó la entrada del usuario, la envolvió en main () {printf ("% d", ...);} y ejecutó un script para compilarlo, vincularlo y ejecutarlo. ¡Él no escribió un analizador! Hoy podría hacerlo en una instrucción SQL SELECT.

Es una herramienta con la que debes jugar, luego guárdala para algún día futuro cuando sea útil.


¡Eso es realmente lo mismo que estaba tratando de implementar! :) Pero luego decidí codificarlo con Perl sin conexión y está funcionando muy bien. ¡Tengo muchas características que estoy pensando agregar!
Jose Faeti

Estoy escribiendo código con hasta 20 capas de transformaciones de lenguaje a lenguaje, sin ningún problema. No es más complicado que tener una profundidad de pila de llamadas de 20 capas. Así que estoy totalmente en desacuerdo con que es una herramienta para " guardarlo para algún día futuro cuando sea útil ": la generación de código siempre es útil.
SK-logic

3

He desarrollado ordenadas meta de programación soluciones con Prolog . Donde la aplicación principal (en C ++, por ejemplo) traduce una definición abstracta de un problema en una aplicación Prolog en tiempo de ejecución, que luego se delega a. A menudo, escribir una funcionalidad equivalente en C ++ llevaría una eternidad.

Creo que este escenario es un excelente caso a favor del argumento de código de escritura de código .


3

¿Qué opinas sobre el tema?

La metaprogramación se asocia más comúnmente con lenguajes no dinámicos, ya que es más difícil lograr ciertos comportamientos (como implementar un ORM) sin muchas líneas de código no productivas y no inteligentes.

Pero incluso en lenguajes más dinámicos como PHP, la generación de código puede ser realmente un salvavidas y aumentar la productividad en gran cantidad. En los marcos modernos, es muy común tener un andamiaje que genere la mayoría de los modelos, formularios, pruebas y acciones comunes para un determinado objeto comercial que usted declara. Es una de las razones por las que los frameworks como Symfony o RoR tienen tanto éxito, esas herramientas de generación de código hacen un código consistente muy rápidamente y aumentan la productividad de los programadores.

En los sitios web, la mayor parte de la interacción gira en torno a cuatro acciones principales:

  • Crear un elemento
  • Recuperar un conjunto de elementos (con posible filtrado)
  • Actualizar un elemento con nuevos atributos
  • Eliminar un conjunto de elementos

Al menos todo lo que gira en torno a estas 4 acciones principales podría y, en mi humilde opinión, DEBE lograrse utilizando herramientas de generación de código para lograr la máxima productividad.

En mi empresa, utilizamos Symfony, y su generador de administración es una herramienta excepcional, que incluso genera código en tiempo de ejecución (y lo almacena en caché), lo que significa que ni siquiera necesitamos usar ningún tipo de tarea o herramienta externa para generar nuevo código, solo necesitamos limpiar nuestro caché. Recomiendo encarecidamente que utilice este tipo de herramienta para operaciones CRUD.

Pero, hacer lo que hicieron los increíbles colaboradores de Symfony no es una tarea fácil. Yo mismo he implementado algunas tareas de generación de código y hacer algo que sea verdaderamente consistente y con una implementación amplia para cubrir la mayoría de los casos de esquina no es fácil.

¿Es algo que realmente aumentará su productividad?

Creo que la metaprogramación es muy muy importante en los niveles inferiores de trabajo (marcos, almacenamiento en caché, compiladores, etc.) pero es algo que debemos abordar con extrema precaución si estamos haciendo cosas en la capa empresarial.

El uso de la generación de código es sin lugar a dudas un importante impulsor de la productividad. Implemente sus propias herramientas de generación de código, no tanto, a menos que esté construyendo un marco usted mismo.

¿Cuáles son algunos buenos recursos sobre el tema, entre libros, blogs, presentaciones de diapositivas, etc.?

El mejor recurso para comprender la programación es siempre un código fuente bueno y bien comentado. Diría que es una buena idea buscar en los generadores de administración de RubyOnRails y Symfony .


3

Si bien muchas respuestas aquí se refieren a lo que comúnmente se conoce como meta programación, de hecho había un campo asociado a la IA conocido como programación automática que trataba sobre la comprensión o la síntesis de programas [1].

Cualquier compilador (o metaprograma, generador de código, traductor, sistema macro, ...) trabaja con transformaciones, generando una salida de una entrada llevando a cabo su algoritmo fijo de transformación. Pero un compilador o metaprograma tradicional, dada una definición, descripción o ejemplo de lo que es ordenar una lista (por ejemplo, [5, 3, 9] => [3,5,9]), crea un algoritmo de clasificación. Tales problemas son del interés de este campo de "programación automática".

[1] - Informe de progreso sobre los sistemas de comprensión del programa ftp://db.stanford.edu/pub/cstr/reports/cs/.../CS-TR-74-444.pdf


2

La meta programación puede ser muy difícil de mantener. Al principio parece elegante, pero cuando comienza a encontrarse con casos de esquina, los errores se detectan tarde (en el código que se ha generado) y todo se convierte en una pesadilla para usar / depurar.

Principalmente escribí código de Python, y en mi experiencia la meta programación siempre es una mala elección con este lenguaje. Siempre puedes refactorizar las cosas para hacerlo con aburridas funciones de lenguaje normal. El resultado es menos funky, pero más fácil de vivir.


Cualquier tipo de código puede ser muy difícil de mantener. Y puede ser muy fácil si se hace de la manera correcta. De hecho, la metaprogramación puede aumentar la capacidad de mantenimiento en órdenes de magnitud. Es probable que su experiencia en Python sea irrelevante para la metaprogramación real, ya que Python no es muy adecuado para esta forma de pensar, con su torpe AST demasiado profundo. Pero incluso con Python, utilicé la biblioteca Tempita con alta eficiencia, y nunca tuve ningún problema de mantenimiento, incluso con un equipo que casi no tenía experiencia previa en Python.
SK-logic

Estoy interesado en su punto sobre Python AST. ¿Has utilizado tempita para metaprogramación?
Simon Bergot el

Esto ( docs.python.org/library/ast.html ) es un AST bastante ad hoc, y el analizador proporciona un árbol no optimizado y superbloqueado, lo que hace que el análisis sea problemático (especialmente con la falta de coincidencia de patrones adecuada en Python). Generar tal AST tampoco es muy conveniente. Utilicé tempita para producir código Python y C (es decir, metaprogramación basada en texto puro), funcionó bien para esa tarea específica (generación de código repetitivo). También solía usar Python para generar código C a partir de algunas descripciones de alto nivel XML.
SK-logic

2

OP pide recursos.

Puede que encuentre nuestro kit de herramientas de reingeniería de software DMS interesante. Es una herramienta de metaprogramación pura, pensada para permitirle construir herramientas de transformación y análisis de programas personalizados.

[Para seguir un comentario a la pregunta de OP, cuando se usa para construir una herramienta de transformación específica, DMS es una línea de productos que escribe código, que escribe código:]

DMS logra esto al ser agnóstico (pero no independiente) de los idiomas de programación de destino. DMS proporciona los servicios estándar necesarios para una amplia variedad de tareas de metaprogramación, de la misma manera que un sistema operativo proporciona una amplia variedad de servicios para tareas de programación estándar. Estos servicios incluyen análisis sólido, construcción automática de árboles de sintaxis abstact, coincidencia de patrones y reescritura en árboles, bibliotecas de tablas de símbolos que administran fácilmente idiomas con reglas de alcance desagradables, como herencia múltiple, flujo de control, flujo de datos, puntos y llamadas Análisis gráfico. Nada de esto es significativo en ausencia de idiomas específicos para procesar, por lo que DMS acepta definiciones de lenguaje que están vinculadas a estas piezas generales de maquinaria, produciendo análisis específicos de idioma, construcción AST, coincidencia / reescritura de patrones específicos de idioma objetivo usando el objetivo- sintaxis de lenguaje,

Y al igual que un sistema operativo, DMS está diseñado para tener muy pocas opiniones o restricciones sobre qué (meta) programas desea escribir, lo que significa que puede usarse para una amplia variedad de propósitos: extraer métricas, encontrar código muerto, implementar tejedores de aspectos, traducir langauges, generando códigos a partir de DSL, reorganizando grandes aplicaciones. (DMS ya se ha utilizado para todas estas tareas).

Se necesitan definiciones de lenguaje robustas si no desea pasar su tiempo codificando todo en el manual de referencia de idioma (piense en lo que esto significa para Java y C ++). DMS resuelve este problema al tener disponible una biblioteca de definiciones completas de idioma. El análogo aquí es algo así como tener una base de datos disponible para su sistema operativo; no tiene que implementar uno de ellos para continuar escribiendo su aplicación centrada en la base de datos.


2

Vea el conjunto de problemas 4 de Philip Greenspun del curso 6.916 del MIT: Ingeniería de software de servicios web innovadores ( http://philip.greenspun.com/teaching/psets/ps4/ps4.adp ).

Su objetivo dice: "Enseñe a los estudiantes las virtudes de los metadatos. Más específicamente, aprenden cómo representar formalmente los requisitos de un servicio web y luego construir un programa informático para generar los programas informáticos que implementan ese servicio".

Este es uno de los conjuntos de problemas que los reclutas potenciales de ArsDigita ( http://en.wikipedia.org/wiki/ArsDigita ) debían resolver durante la primera burbuja.

El libro "SQL para Web Nerds", referencias de Philip en el pset, se ha movido a ( http://philip.greenspun.com/sql/ ).


2

Alrededor de 2001 comencé a trabajar en un proyecto que hacía un uso extensivo de objetos comerciales y objetos de datos. Iba a construir el sitio web front-end, pero me colgaron haciendo girar mis pulgares porque la capa empresarial y la capa de acceso a datos no estaban completamente desarrolladas. Después de un par de semanas de eso, comencé a analizar lo que estaban haciendo esas capas. Básicamente, estaban exponiendo los datos devueltos de los procedimientos almacenados como colecciones de objetos con propiedades correspondientes a los campos en los datos, o estaban tomando parámetros de entrada y enviándolos a procedimientos almacenados para guardarlos en las tablas de la base de datos. Se estaba produciendo una gran cantidad de serialización / deserialización entre las dos capas, había Microsoft Transaction Server involucrado, una biblioteca de tipo IDL / ODL ... pero todo encajaba en un patrón.

2 semanas más tarde, tuve un generador de código resuelto que eliminaría IDL / ODL, y también eliminaría los objetos comerciales y de datos. Al chico que construyó el negocio y los objetos de la capa de datos le tomó 2 años llegar al punto de depurar y probar estos objetos. En 2 semanas, con la generación de código, obtuvimos el mismo resultado, pero como todo se generó, estuvo bastante libre de errores.

Ese generador de código (herramienta CASE de nivel inferior) me siguió a través de muchas iteraciones diferentes, durante aproximadamente 8 a 10 años, porque el principio era muy simple: estás haciendo algo que debes hacer cuando hablas con bases de datos, es bastante codificación repetitiva, y una vez que lo hagas bien, ya no tienes que preocuparte más por él.

Entonces, sí: use un generador de código, particularmente cuando la codificación es repetitiva y se ajusta a un patrón bien definido.

He conocido a personas que usan macros RegX para hacer cosas similares, o usar fórmulas de Excel para hacer cosas similares (yo también hago esto).


2

Un ejemplo de metaprogramación

Tengo una biblioteca de autorizaciones Ruby llamada Autoridad . Permite a los desarrolladores hacer preguntas en su aplicación con métodos como current_user.can_read?(@post)y @post.readable_by?(current_user). Estas preguntas son respondidas por clases autorizadas centralizadas.

Esta es la parte crucial: la autoridad no sabe qué métodos definir hasta que ve la configuración del usuario . La configuración del usuario puede contener:

config.abilities =  {
  ...
  :read      => 'readable',
  :microwave => 'microwavable',  # user-defined
  ...
}

En ese caso, debe haber un método como current_user.can_microwave?(@post).

La metaprogramación hace esto posible: después de leer la configuración, sé qué métodos definir :

Authority.verbs.each do |verb|
  class_eval <<-RUBY, __FILE__, __LINE__ + 1 # allows for a nice bracktrace
    def can_#{verb}?(resource)
      resource.#{Authority.abilities[verb]}_by?(self)
    end
  RUBY
end
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.