Si se puede generar algo, entonces eso es información, no código.
Dado eso, ¿no es toda esta idea de generación de código fuente un malentendido? Es decir, si hay un generador de código para algo, entonces ¿por qué no hacer de ese algo una función adecuada que pueda recibir los parámetros requeridos y realizar la acción correcta que el código "generado" habría hecho?
Si se hace por razones de rendimiento, eso suena como una deficiencia del compilador.
Si se está haciendo para unir dos idiomas, entonces eso suena como una falta de biblioteca de interfaz.
¿Me estoy perdiendo de algo?
Sé que el código también es información. Lo que no entiendo es, ¿por qué generar código fuente ? ¿Por qué no convertirlo en una función que pueda aceptar parámetros y actuar sobre ellos?
flex
o un analizador generado porbison
seguramente será más predecible, más correcto y, a menudo, más rápido de ejecutar que los equivalentes escritos a mano en C; y construido a partir de mucho menos código (por lo tanto, también es menos trabajo de mantenimiento).Respuestas:
Técnicamente, si generamos código, no es fuente, incluso si es texto que los humanos puedan leer. El código fuente es un código original, generado por un ser humano u otra inteligencia verdadera, no traducido mecánicamente y no reproducible inmediatamente desde la fuente (verdadera) (directa o indirectamente).
Yo diría que todo son datos de todos modos. Incluso el código fuente. Especialmente el código fuente! El código fuente son solo datos en un lenguaje diseñado para realizar tareas de programación. Estos datos deben traducirse, interpretarse, compilarse, generarse según sea necesario en otras formas, de datos, algunos de los cuales pueden ser ejecutables.
El procesador ejecuta instrucciones sin memoria. La misma memoria que se usa para los datos. Antes de que el procesador ejecute instrucciones, el programa se carga en la memoria como datos .
Entonces, todo son datos , incluso el código .
Está perfectamente bien tener múltiples pasos en la compilación, uno de los cuales puede ser la generación de código intermedio como texto.
Esa es una forma, pero hay otras.
No todos los formularios de texto están destinados al consumo humano. En particular, el código generado (como texto) generalmente está destinado al consumo del compilador, no al consumo humano.
El código fuente se considera el original: el maestro: lo que editamos y desarrollamos; lo que archivamos usando el control del código fuente. El código generado, incluso cuando el texto es legible para humanos, generalmente se regenera a partir del código fuente original . El código generado, en general, no tiene que estar bajo el control de la fuente, ya que se regenera durante la compilación.
fuente
Razonamiento práctico
A partir de esta edición, supongo que está preguntando en un nivel bastante práctico, no en informática teórica.
La razón clásica para generar código fuente en lenguajes estáticos como Java es que los lenguajes así simplemente no vienen con herramientas fáciles de usar en el lenguaje para hacer cosas muy dinámicas. Por ejemplo, en los días formativos de Java, simplemente no era posible crear fácilmente una clase con un nombre dinámico (que coincida con un nombre de tabla de una base de datos) y métodos dinámicos (atributos coincidentes de esa tabla) con tipos de datos dinámicos (coincidencia los tipos de dichos atributos). Especialmente porque Java le da mucha importancia, es decir, garantías, a poder detectar errores de tipo en tiempo de compilación.
Entonces, en tal configuración, un programador solo puede crear código Java y escribir muchas líneas de código manualmente. A menudo, el programador encontrará que cada vez que cambia una tabla, tiene que regresar y cambiar el código para que coincida; y si olvida eso, suceden cosas malas. Por lo tanto, el programador llegará al punto en el que escribe algunas herramientas que lo hacen por él. Y, por lo tanto, el camino comienza a generar códigos cada vez más inteligentes.
(Sí, podría generar el código de bytes sobre la marcha, pero programar tal cosa en Java no sería algo que un programador aleatorio haría solo entre escribir unas pocas líneas de código de dominio).
Compare esto con los lenguajes que son muy dinámicos, por ejemplo, Ruby, que consideraría la antítesis de Java en la mayoría de los aspectos (tenga en cuenta que lo digo sin valorar ninguno de los dos enfoques; simplemente son diferentes). Aquí es 100% normal y estándar generar dinámicamente clases, métodos, etc. en tiempo de ejecución, y lo más importante, el programador puede hacerlo trivialmente en el código, sin pasar a un nivel "meta". Sí, cosas como Ruby on Rails vienen con la generación de código, pero descubrimos en nuestro trabajo que básicamente lo usamos como una especie de "modo tutorial" avanzado para nuevos programadores, pero después de un tiempo se vuelve superfluo (ya que hay muy poco código escribir en ese ecosistema que cuando sabes lo que estás haciendo, escribirlo manualmente se vuelve más rápido que limpiar el código generado).
Estos son solo dos ejemplos prácticos del "mundo real". Entonces tienes lenguajes como LISP donde el código es datos, literalmente. Por otro lado, en los lenguajes compilados (sin un motor de tiempo de ejecución como Java o Ruby), existe (o no he estado al día con las características modernas de C ++ ...) simplemente no hay concepto de definir nombres de clases o métodos en tiempo de ejecución, por lo que la generación de código, el proceso de compilación es la herramienta elegida para la mayoría de las cosas (otros ejemplos más específicos de C / C ++ serían cosas como flex, yacc, etc.).
fuente
Porque programar con tarjetas perforadas (o códigos alt en el bloc de notas ) es una molestia.
Cierto. No me importa el rendimiento a menos que me vean obligado a hacerlo.
Hmm, no tengo idea de lo que estás hablando.
Mire, es así: el código fuente generado y retenido es siempre y para siempre un dolor de cabeza. Existe por una sola razón. Alguien quiere trabajar en un idioma mientras alguien más insiste en trabajar en otro y ninguno de los dos puede molestarse en descubrir cómo interactuar entre ellos para que uno de ellos descubra cómo convertir su idioma favorito en el idioma impuesto para que puedan hacer lo que quieran. ellos quieren.
Lo cual está bien hasta que tenga que mantenerlo. En ese momento todos ustedes pueden ir a morir.
¿Es un anti patrón? Suspiro, no. Muchos idiomas ni siquiera existirían si no estuviéramos dispuestos a despedirnos de las deficiencias de los idiomas anteriores y generar el código de los idiomas más antiguos es la cantidad de nuevos idiomas que comienzan.
Es una base de código que se deja en un mosaico de monstruos de Frankenstein medio convertido que no puedo soportar. El código generado es un código intocable. Odio mirar el código intocable. Sin embargo, la gente sigue revisándolo. ¿POR QUÉ? También podrías estar registrando el ejecutable.
Bueno, ahora estoy despotricando. Mi punto es que todos estamos "generando código". Es cuando tratas el código generado como el código fuente que me estás volviendo loco. Solo porque parece que el código fuente no lo convierte en código fuente.
fuente
/etc/
archivos en Unix, etc.El caso de uso más frecuente para los generadores de código con los que tuve que trabajar en mi carrera fueron los generadores que
tomó una metadescripción de alto nivel para algún tipo de modelo de datos o esquema de base de datos como entrada (tal vez un esquema relacional o algún tipo de esquema XML)
y produjo código CRUD de placa de caldera para clases de acceso a datos como salida, y tal vez cosas adicionales como los correspondientes SQL o documentación.
El beneficio aquí es que de una línea de una especificación de entrada corta obtienes de 5 a 10 líneas de código depurable, seguro de tipo, libre de errores (se supone que la salida de los generadores de código está madura) que de lo contrario tuvo que implementar y mantener manualmente. Puede imaginar cuánto reduce esto el mantenimiento y el esfuerzo de evolución.
Déjame responder también a tu pregunta inicial
No, no la generación del código fuente per se, pero de hecho hay algunas trampas. Como se indica en The Pragmatic Programmer , uno debe evitar el uso de un generador de código cuando produce código que es difícil de entender . De lo contrario, los mayores esfuerzos para usar o depurar este código pueden superar fácilmente el esfuerzo ahorrado al no escribir el código manualmente.
También me gustaría agregar que la mayoría de las veces es una buena idea separar físicamente las partes generadas del código del código escrito manualmente de una manera que la regeneración no sobrescriba ningún cambio manual. Sin embargo, también me he ocupado de la situación más de una vez en la que la tarea consistía en migrar un código escrito en el lenguaje antiguo X a otro lenguaje más moderno Y, con la intención de realizar el mantenimiento posterior en el lenguaje Y. Este es un uso válido caso para la generación de código de una sola vez.
fuente
He encontrado dos casos de uso para el código generado (en el momento de la compilación, y nunca registrado):
fuente
Sussmann tenía mucho más interesante que decir sobre tales cosas en su clásico "Estructura e interpretación de programas de computadora", principalmente sobre la dualidad de datos de código.
Para mí, el uso principal de la generación de código adhoc es hacer uso de un compilador disponible para convertir algún pequeño lenguaje específico de dominio en algo que pueda vincular a mis programas. Piense en BNF, piense en ASN1 (en realidad, no lo haga, es feo), piense en las hojas de cálculo del diccionario de datos.
Los lenguajes específicos de dominio triviales pueden ahorrar mucho tiempo, y generar algo que pueda compilarse con herramientas de lenguaje estándar es el camino a seguir al crear tales cosas, que preferiría editar, un analizador pirateado no trivial en cualquier idioma nativo que sea escritura, o el BNF para uno generado automáticamente?
Al generar texto que luego se envía a algún compilador del sistema, obtengo toda la optimización de compiladores y la configuración específica del sistema sin tener que pensarlo.
Estoy usando efectivamente el lenguaje de entrada del compilador como otra representación intermedia, ¿cuál es el problema? Los archivos de texto no son inherentemente código fuente, pueden ser un IR para un compilador , y si se parecen a C o C ++ o Java o lo que sea, ¿a quién le importa?
Ahora, si tiene dificultades para pensar , puede editar la SALIDA del analizador de idioma de juguetes, lo que claramente decepcionará la próxima vez que alguien edite los archivos de idioma de entrada y se reconstruya, la respuesta es no enviar el IR generado automáticamente al repositorio, téngalo generado por su cadena de herramientas (y evite tener esas personas en su grupo de desarrollo, por lo general son más felices trabajando en marketing).
Esto no es tanto un fracaso de la expresividad en nuestros idiomas, como una expresión del hecho de que a veces puede obtener (o masajear) partes de la especificación en una forma que se puede convertir automáticamente en código, y que generalmente generará mucho menos errores y ser mucho más fácil de mantener. Si puedo darles a nuestros muchachos de prueba y configuración una hoja de cálculo que pueden ajustar y una herramienta que luego ejecutan que toma esos datos y escupe un archivo hexadecimal completo para el flash en mi ECU, entonces es un gran ahorro de tiempo que alguien traduzca manualmente la última configuración en un conjunto de constantes en el idioma del día (Completo con errores tipográficos).
Lo mismo con construir modelos en Simulink y luego generar C con RTW y luego compilar para apuntar con cualquier herramienta que tenga sentido, el C intermedio es ilegible, ¿y qué? El material de alto nivel de Matlab RTW solo necesita conocer un subconjunto de C, y el compilador de C se encarga de los detalles de la plataforma. El único momento en que un ser humano tiene que arrastrarse a través del C generado es cuando los scripts RTW tienen un error, y ese tipo de cosas es mucho más fácil de depurar con un IR legible nominalmente humano que con un árbol de análisis binario.
Por supuesto, puede escribir tales cosas para generar bytecode o incluso código ejecutable, pero ¿por qué haría eso? Tenemos herramientas para convertir un IR a esas cosas.
fuente
Respuesta pragmática: ¿la generación de código es necesaria y útil? ¿Proporciona algo que es realmente muy útil y necesario para la base de código patentada, o parece que simplemente crea otra forma de hacer las cosas de una manera que contribuya con una sobrecarga intelectual para obtener resultados subóptimos?
Si tiene que hacer esta pregunta y no hay una respuesta clara, entonces probablemente la generación de código sea superflua y simplemente contribuya con exotismo y una gran cantidad de sobrecarga intelectual a su base de código.
Mientras tanto, si toma algo como OpenShadingLanguage: https://github.com/imageworks/OpenShadingLanguage
... entonces tales preguntas no necesitan ser planteadas ya que son respondidas de inmediato por los impresionantes resultados.
En tal caso, no necesita cuestionar la existencia del generador de código. Si trabaja en este tipo de dominio de efectos visuales, su respuesta inmediata suele ser más de "¡cállate y toma mi dinero!" o "wow, también necesitamos hacer algo como esto".
fuente
No, generar código intermedio no es un antipatrón. La respuesta a la otra parte de su pregunta, "¿Por qué hacerlo?", Es una pregunta muy amplia (y separada), aunque de todos modos daré algunas razones.
Ramificaciones históricas de nunca tener código intermedio legible por humanos
Tomemos C y C ++ como ejemplos, ya que se encuentran entre los lenguajes más famosos.
Debe tener en cuenta que la procesión lógica de compilar código C no genera código de máquina, sino código de ensamblaje legible por humanos. Del mismo modo, los viejos compiladores de C ++ solían compilar físicamente el código de C ++ en código C. En esa cadena de eventos, puede compilar desde el código legible por humanos 1 al código legible por humanos 2 al código legible por humanos 3 al código de máquina. "¿Por qué?" Por qué no?
Si nunca se generó un código intermedio legible para humanos, es posible que ni siquiera tengamos C o C ++. Esa es ciertamente una posibilidad; las personas toman el camino de menor resistencia a sus objetivos, y si algún otro lenguaje ganó fuerza primero debido al estancamiento del desarrollo de C, C podría haber muerto mientras aún era joven. Por supuesto, podría argumentar "Pero entonces quizás estaríamos usando otro idioma, y quizás sería mejor". Tal vez, o tal vez sería peor. O tal vez todos todavía estaríamos escribiendo en asamblea.
¿Por qué usar código intermedio legible por humanos?
Ejemplo
He trabajado en proyectos antes donde el código necesita ser generado en base a datos o información en algún otro documento. Por ejemplo, un proyecto tenía todos sus mensajes de red y datos constantes definidos en una hoja de cálculo y una herramienta que iría a través de la hoja de cálculo y generaría una gran cantidad de código C ++ y Java que nos permite trabajar con esos mensajes.
No digo que esa sea la mejor manera de configurar ese proyecto (no era parte de su inicio), pero eso fue lo que tuvimos, y fueron cientos (quizás incluso miles, no estoy seguro) de estructuras, objetos y constantes. que se estaban generando; en ese punto, probablemente sea demasiado tarde para intentar rehacerlo en algo como Rhapsody. Pero incluso si se rehizo en algo como Rhapsody, de todos modos todavía tenemos código generado a partir de Rhapsody .
Además, tener todos esos datos en una hoja de cálculo era bueno de una manera: nos permitía representar los datos de una manera que no podríamos tener si solo estuviera en los archivos de código fuente.
Ejemplo 2
Cuando trabajé en la construcción del compilador, utilicé la herramienta Antlr para hacer mi lexing y análisis. Especifiqué una gramática de lenguaje, luego usé la herramienta para escupir una tonelada de código en C ++ o Java, luego usé ese código generado junto con mi propio código y lo incluí en la compilación.
¿De qué otra forma debería haberse hecho eso? Quizás podrías encontrar otra forma; Probablemente hay otras formas. Pero para ese trabajo, las otras formas no habrían sido mejores que el código lex / parse generado que tenía.
fuente
Lo que te falta es reutilizar .
Tenemos una herramienta increíble para convertir el texto del código fuente en binario, llamado compilador. Sus entradas están bien definidas (¡por lo general!), Y ha trabajado mucho para refinar cómo funciona la optimización. Si realmente desea utilizar el compilador para llevar a cabo algunas operaciones, desea utilizar un compilador existente y no escribir el suyo propio.
Mucha gente inventa nuevos lenguajes de programación y escribe sus propios compiladores. Casi sin excepción, todos lo hacen porque disfrutan el desafío, no porque necesitan las características que proporciona ese lenguaje. Todo lo que hacen se puede hacer en otro idioma; simplemente están creando un nuevo lenguaje porque les gustan esas características. Sin embargo, lo que no les conseguirá es un compilador bien optimizado, rápido, eficiente y optimizador. Les dará algo que puede convertir el texto en binario, claro, pero no será tan bueno como todos los compiladores existentes .
El texto no es solo algo que los humanos leen y escriben. Las computadoras están perfectamente en casa con texto también. De hecho, formatos como XML (y otros formatos relacionados) son exitosos porque usan texto sin formato. Los formatos de archivos binarios a menudo son oscuros y están poco documentados, y un lector no puede descubrir fácilmente cómo funcionan. XML es relativamente autodocumentado, lo que facilita a las personas escribir código que utiliza archivos con formato XML. Y todos los lenguajes de programación están configurados para leer y escribir archivos de texto.
Entonces, supongamos que desea agregar alguna nueva instalación para facilitarle la vida. Quizás es una herramienta de diseño de GUI. Quizás son las interfaces de señales y ranuras las que proporciona Qt . Tal vez sea la forma en que Code Composer Studio de TI le permite configurar el dispositivo con el que está trabajando y extraer las bibliotecas correctas en la compilación. Quizás esté tomando un diccionario de datos y autodefiniendo typedefs y definiciones de variables globales (sí, esto sigue siendo algo muy importante en el software integrado). Sea lo que sea, la forma más eficiente de aprovechar su compilador existente es crear una herramienta que tome su configuración de lo que sea y produzca automáticamente el código en el idioma que elija.
Es fácil de desarrollar y de probar, porque sabes lo que está pasando y puedes leer el código fuente que escupe. No necesita gastar muchos años en construir un compilador para rivalizar con GCC. No es necesario que aprenda un idioma completamente nuevo ni que otras personas lo hagan. Todo lo que necesita hacer es automatizar esta pequeña área, y todo lo demás permanece igual. Trabajo hecho.
fuente
Una respuesta un poco más pragmática, centrándose en por qué y no en qué es y qué no es el código fuente. Tenga en cuenta que la generación de código fuente es parte del proceso de compilación en todos estos casos, por lo que los archivos generados no deberían llegar al control de origen.
Interoperabilidad / simplicidad
Tomemos los Protocol Buffers de Google, un buen ejemplo: usted escribe una única descripción de protocolo de alto nivel que luego puede usarse para generar la implementación en varios idiomas, a menudo diferentes partes del sistema se escriben en diferentes idiomas.
Implementación / razones técnicas
Tome TypeScript: los navegadores no pueden interpretarlo, por lo que el proceso de compilación utiliza un transpilador ( traductor de código a código) para generar JavaScript. De hecho, muchos lenguajes compilados nuevos o esotéricos comienzan con la transpiración a C antes de obtener un compilador adecuado.
Facilidad de uso
Para proyectos incrustados (piense en IoT) escritos en C y usando solo un solo binario (RTOS o sin SO), es bastante fácil generar una matriz C con los datos para compilar como si fuera un código fuente normal, en oposición a vincularlos directamente como recursos
Editar
Ampliación en protobuf: la generación de código permite que los objetos generados sean clases de primera clase en cualquier idioma. En un lenguaje compilado, un analizador genérico necesariamente devolvería una estructura clave-valor, lo que significa que necesita mucho código repetitivo, se perderá algunas comprobaciones en tiempo de compilación (en claves y tipos de valores en particular), obtendrá un peor rendimiento y Sin completar el código. Imagine todos aquellos
void*
en C o tan grandesstd::variant
en C ++ (si tiene C ++ 17), algunos lenguajes pueden no tener tal característica en absoluto.fuente
Es una solución para un lenguaje de programación insuficientemente expresivo. No es necesario generar código en un lenguaje que contenga metaprogramación incorporada adecuada.
fuente
La generación del código fuente no siempre es un antipatrón. Por ejemplo, actualmente estoy escribiendo un marco que, según la especificación dada, genera código en dos idiomas diferentes (Javascript y Java). El marco utiliza el Javascript generado para registrar las acciones del navegador del usuario, y usa el código Java en Selenium para ejecutar realmente la acción cuando el marco está en modo de reproducción. Si no usara la generación de código, tendría que asegurarme manualmente de que ambos estén siempre sincronizados, lo cual es engorroso y también es una duplicación lógica de alguna manera.
Sin embargo, si uno está utilizando la generación de código fuente para reemplazar características como los genéricos, entonces es antipatrón.
fuente
¿Quizás un buen ejemplo donde el código intermediario resultó ser la razón del éxito? Puedo ofrecerte HTML.
Creo que era importante que el HTML fuera simple y estático: facilitaba la creación de navegadores, permitía iniciar navegadores móviles antes de tiempo, etc. Como demostraron más experimentos (applets de Java, Flash), los lenguajes más complejos y potentes conducen a más problemas . Resulta que los usuarios están realmente en peligro por los applets de Java y visitar esos sitios web era tan seguro como probar los juegos descargados a través de DC ++. El HTML simple, por otro lado, es lo suficientemente inofensivo como para permitirnos visitar cualquier sitio con una creencia razonable en la seguridad de nuestro dispositivo.
Sin embargo, HTML no estaría cerca de donde está ahora si no fuera generado por computadora. Mi respuesta ni siquiera aparecería en esta página hasta que alguien la reescribiera manualmente desde la base de datos en un archivo HTML. Afortunadamente, puedes hacer HTML utilizable en casi cualquier lenguaje de programación :)
¿Te imaginas una mejor manera de mostrar la pregunta y todas las respuestas y comentarios al usuario que usando HTML como un código intermedio generado?
fuente
Porque es más rápido y fácil (y menos propenso a errores) que escribir el código manualmente, especialmente para tareas tediosas y repetitivas. También puede usar la herramienta de alto nivel para verificar y validar su diseño antes de escribir una sola línea de código.
Casos de uso común:
En cuanto a su "por qué no simplemente convertirlo en una función y pasarle parámetros directamente", tenga en cuenta que ninguno de los anteriores son entornos de ejecución en sí mismos. No hay forma de vincular su código contra ellos.
fuente
A veces, su lenguaje de programación simplemente no tiene las instalaciones que desea, lo que hace que sea realmente imposible escribir funciones o macros para hacer lo que desea. O tal vez podrías hacer lo que quieras, pero el código para escribirlo sería feo. Un simple script de Python (o similar) puede generar el código requerido como parte de su proceso de compilación, que luego ingresa
#include
en el archivo fuente real.¿Cómo se esto? Porque es una solución a la que he llegado varias veces cuando trabajo con varios sistemas diferentes, más recientemente SourcePawn. Una secuencia de comandos Python simple que analiza una línea simple de código fuente y produce dos o tres líneas de código generado es mucho mejor que crear manualmente el código generado, cuando terminas con dos docenas de tales líneas (creando todos mis cvars).
Código fuente demostrativo / de ejemplo disponible si la gente lo quiere.
fuente
Se requiere forma de texto para que los humanos puedan consumirlo fácilmente. Las computadoras también procesan el código en forma de texto con bastante facilidad. Por lo tanto, el código generado debe generarse en la forma que sea más fácil de generar y más fácil de consumir por las computadoras, y que a menudo es texto legible.
Y cuando genera código, el proceso de generación de código en sí a menudo debe ser depurado, por humanos. Es muy, muy útil si el código generado es legible por humanos para que los humanos puedan detectar problemas en el proceso de generación de código. Alguien tiene que escribir el código para generar código, después de todo. No sucede de la nada.
fuente
Generando código, solo una vez
No toda la generación de código fuente es un caso de generar algún código, y luego nunca tocarlo; luego regenerarlo desde la fuente original cuando necesita actualizarse.
A veces genera código solo una vez, y luego descarta la fuente original, y en adelante mantiene la nueva fuente.
Esto a veces sucede cuando se transfiere código de un idioma a otro. Particularmente si uno no espera querer trasladar más adelante nuevos cambios en el original (por ejemplo, el código del idioma antiguo no se mantendrá, o en realidad está completo (por ejemplo, en el caso de alguna funcionalidad matemática)).
Un caso común es que escribir un generador de código para hacer esto podría traducir el 90% del código correctamente. y luego ese último 10% debe repararse a mano. Lo cual es mucho más rápido que traducir 100% a mano.
Tales generadores de código son a menudo muy diferentes al tipo de generadores de código que
f2c
producen los traductores de lenguaje completo (como Cython o ). Dado que el objetivo es hacer mantener el código una vez. A menudo se hacen como 1 apagado, para hacer exactamente lo que tienen que hacer. En muchos sentidos, es la versión de siguiente nivel del uso de una expresión regular / buscar-reemplazar al código de puerto. "Portado asistido por herramientas" se podría decir.Generando código, solo una vez, desde, por ejemplo, un raspado de sitio web.
Estrechamente relacionado es si genera el código de alguna fuente a la que no desea acceder nuevamente. Por ejemplo, si las acciones necesarias para generar el código no son repetibles o consistentes, o realizarlas es costoso. Estoy trabajando en un par de proyectos en este momento: DataDeps.jl y DataDepsGenerators.jl .
DataDeps.jl ayuda a los usuarios a descargar datos (como conjuntos de datos ML estándar). Para hacer esto necesita lo que llamamos un RegistrationBlock. Ese es un código que especifica algunos metadatos, como dónde descargar los archivos y una suma de verificación, y un mensaje que explica al usuario cualquier término / codificación / cuál es el estado de la licencia de los datos.
Escribir esos bloques puede ser molesto. Y esa información a menudo está disponible en (estructurada o no estructurada) en los sitios web donde se alojan los datos. Por lo tanto, DataDepsGenerators.jl utiliza un raspador web para generar el RegistrationBlockCode, para algunos sitios que alojan una gran cantidad de datos.
Puede que no los genere correctamente. Entonces, el desarrollador que usa el código generado puede y debe verificarlo y corregirlo. Lo más probable es que quieran asegurarse de que no haya descartado la información de licencia, por ejemplo.
Es importante destacar que los usuarios / desarrolladores que trabajan con DataDeps.jl no necesitan instalar o usar el webcraper para usar el código RegistrationBlock que se generó. (Y no necesitar descargar e instalar un raspador de web ahorra un poco de tiempo, particularmente para las ejecuciones de CI)
Generar código fuente una vez no es un antipatrón. y normalmente no se puede reemplazar con metaprogramación.
fuente
f2c
+cc
), pero el código resultante no era realmente un buen punto de partida para una versión en C del programa, AFAIK.f2c
pasado)sed
va un largo camino, pero a veces uno necesita un poco más de poder expresivo. La línea entre la lógica del programa y los datos suele ser buena. A veces la distinción no es útil. JSON es (/ was) solo el código del constructor de objetos javascript. En mi ejemplo, yo también estoy generando código de constructor de objeto (es que los datos tal vez (tal vez no ya que a veces tiene llamadas de función) ¿Es mejor tratados como código de sí?.?.)La generación del código "fuente" es una indicación de una deficiencia del lenguaje que se genera. ¿Usar herramientas para superar esto es un antipatrón? Absolutamente no, déjame explicarte.
Por lo general, la generación de código se usa porque existe una definición de nivel superior que puede describir el código resultante mucho menos detallado que el lenguaje de nivel inferior. Por lo tanto, la generación de código facilita la eficiencia y la brevedad.
Cuando escribo c ++, lo hago porque me permite escribir código más eficiente que usar ensamblador o código de máquina. Todavía el código de máquina es generado por el compilador. Al principio, c ++ era simplemente un preprocesador que generaba código C. Los lenguajes de propósito general son excelentes para generar un comportamiento de propósito general.
De la misma manera, al usar un DSL (lenguaje específico de dominio) es posible escribir conciso, pero tal vez el código se constriñe a una tarea específica. Esto hará que sea menos complicado generar el comportamiento correcto del código. Recuerde que el código es medios para y al final . Lo que busca un desarrollador es una forma eficiente de generar comportamiento.
Idealmente, el generador puede crear código rápido a partir de una entrada que sea más fácil de manipular y comprender. Si esto se cumple, no usar un generador es un antipatrón . Este antipatrón generalmente proviene de la noción de que el código "puro" es "más limpio", de la misma manera que un trabajador de la madera u otro artesano podría considerar el uso de herramientas eléctricas o el uso de CNC para "generar" piezas de trabajo (piense en dorado martillo )
Por otro lado, si la fuente del código generado es más difícil de mantener o generar código que no es lo suficientemente eficiente, el usuario cae en la trampa de usar las herramientas incorrectas (en algún momento debido al mismo martillo dorado ).
fuente
La generación del código fuente absolutamente significa que el código generado son datos. Pero son datos de primera clase, datos que el resto del programa puede manipular.
Los dos tipos de datos más comunes que conozco que están integrados en el código fuente son la información gráfica sobre ventanas (número y ubicación de varios controles) y ORM. En ambos casos, la integración a través de la generación de código facilita la manipulación de los datos, ya que no tiene que pasar por pasos "especiales" adicionales para usarlos.
Al trabajar con las Macs originales (1984), las definiciones de diálogo y ventana se crearon utilizando un editor de recursos que mantuvo los datos en formato binario. Usar estos recursos en su aplicación fue más difícil de lo que hubiera sido si el "formato binario" hubiera sido Pascal.
Entonces, no, la generación del código fuente no es un antipatrón, permite que los datos formen parte de la aplicación, lo que facilita su uso.
fuente
La generación de código es un antipatrón cuando cuesta más de lo que logra. Esta situación ocurre cuando la generación se lleva a cabo de A a B, donde A es casi el mismo lenguaje que B, pero con algunas extensiones menores que podrían hacerse simplemente codificando en A con menos esfuerzo que todas las herramientas personalizadas y la preparación de etapas para A a B .
La compensación es más prohibitiva contra la generación de código en lenguajes que no tienen instalaciones de metaprogramación (macros estructurales) debido a las complicaciones e insuficiencias de lograr la metaprogramación a través de la puesta en escena del procesamiento de texto externo.
El intercambio pobre también podría tener que ver con la cantidad de uso. El lenguaje A podría ser sustancialmente diferente del B, pero todo el proyecto con su generador de código personalizado solo usa A en uno o dos lugares pequeños, de modo que la cantidad total de complejidad (pequeños bits de A, más el generador de código A -> B, más la puesta en escena circundante de construcción) excede la complejidad de una solución que se acaba de hacer en B.
Básicamente, si nos comprometemos con la generación de código, probablemente deberíamos "ir a lo grande o ir a casa": hacer que tenga una semántica sustancial, y usarla mucho, o no molestarnos.
fuente
No vi esto claramente establecido (lo vi tocado por una o dos respuestas, pero no parecía muy claro)
Generar código (como dijiste, como si fueran datos) no es un problema, es una forma de reutilizar un compilador para un propósito secundario.
Editar el código generado es uno de los antipatrones más insidiosos, malvados y horribles que jamás haya encontrado. No hagas esto.
En el mejor de los casos, la edición del código generado extrae un montón de código deficiente en su proyecto (el conjunto COMPLETO de código ahora es realmente CÓDIGO FUENTE - ya no son datos). En el peor de los casos, el código extraído en su programa es altamente redundante, basura mal nombrada que es casi completamente imposible de mantener.
Supongo que una tercera categoría es el código que usa una vez (¿generador de interfaz gráfica de usuario?) Y luego edite para ayudarlo a comenzar / aprender. Esto es un poco de cada uno: PUEDE ser una buena manera de comenzar, pero su generador de GUI estará destinado a usar código "Generable" que no será un gran comienzo para usted como programador. Además, puede ser Está tentado a usarlo nuevamente para una segunda GUI, lo que significa introducir código SOURCE redundante en su sistema.
Si su herramienta es lo suficientemente inteligente como para no permitir ninguna edición del código generado, hágalo. Si no, lo llamaría uno de los peores antipatrones que existen.
fuente
El código y los datos son: información.
Los datos son la información exactamente en la forma que necesita (y valor). El código también es información, pero de forma indirecta o intermedia. En esencia, el código también es una forma de datos.
Más específicamente, el código es información para que las máquinas descarguen a los humanos del procesamiento de la información por sí mismos.
Descargar a los humanos del procesamiento de la información es el motivo más importante. Los pasos intermedios son aceptables siempre que faciliten la vida. Es por eso que existen herramientas intermedias de mapeo de información. Como generadores de código, compiladores, transpiladores, etc.
Digamos que alguien le ofrece dicha función de mapeo, cuya implementación es oscura para usted. Mientras la función funcione según lo prometido, ¿le importaría si internamente genera código fuente o no?
fuente
En la medida en que estipule más adelante que ese código son datos, su propuesta se reduce a "Si se puede generar algo, entonces esa cosa no es código". ¿Diría, entonces, que el código de ensamblaje generado por un compilador de C no es código? ¿Qué pasa si coincide exactamente con el código de ensamblaje que escribo a mano? Puedes ir allí si lo deseas, pero no iré contigo.
Comencemos con una definición de "código". Sin ser demasiado técnico, una definición bastante buena para los propósitos de esta discusión sería "instrucciones accionables por la máquina para realizar un cálculo".
Bueno, sí, su propuesta inicial es que el código no se puede generar, pero rechazo esa propuesta. Si acepta mi definición de "código", entonces no debería haber ningún problema conceptual con la generación de código en general.
Bueno, esa es una pregunta completamente diferente, sobre la razón para emplear la generación de código, en lugar de sobre su naturaleza. Está proponiendo la alternativa de que, en lugar de escribir o usar un generador de código, se escriba una función que calcule el resultado directamente. ¿Pero en qué idioma? Atrás quedaron los días en que alguien escribió directamente en el código de máquina, y si escribe su código en cualquier otro idioma, entonces depende de un generador de código en forma de compilador y / o ensamblador para producir un programa que realmente se ejecute.
¿Por qué, entonces, prefieres escribir en Java o C o Lisp o lo que sea? Incluso ensamblador? Afirmo que es al menos en parte porque esos lenguajes proporcionan abstracciones para los datos y las operaciones que hacen que sea más fácil expresar los detalles del cálculo que desea realizar.
Lo mismo es cierto para la mayoría de los generadores de código de nivel superior, también. Los casos prototípicos son probablemente generadores de escáner y analizador sintáctico como
lex
yyacc
. Sí, puede escribir un escáner y un analizador directamente en C o en algún otro lenguaje de programación de su elección (incluso código máquina sin formato), y a veces uno lo hace. Pero para un problema de complejidad significativa, el uso de un lenguaje de propósito especial de nivel superior como lex's o yacc's hace que el código escrito a mano sea más fácil de escribir, leer y mantener. Por lo general, también es mucho más pequeño.También debe considerar qué quiere decir exactamente con "generador de código". Consideraría el preprocesamiento de C y la creación de instancias de plantillas de C ++ como ejercicios en la generación de código; ¿te opones a esto? Si no, entonces creo que necesitarás realizar algunas gimnasias mentales para racionalizar la aceptación de esas pero rechazando otros sabores de generación de código.
¿Por qué? Básicamente está postulando que uno debería tener un programa universal al que el usuario alimente datos, algunos clasificados como "instrucciones" y otros como "entrada", y que proceda a realizar el cálculo y emitir más datos que llamamos "salida". (Desde cierto punto de vista, uno podría llamar a un programa tan universal como "sistema operativo"). Pero, ¿por qué supone que un compilador debería ser tan efectivo para optimizar un programa de propósito general como lo es para optimizar un programa más especializado? ¿programa? Los dos programas tienen características diferentes y capacidades diferentes.
Dices eso como si tener una biblioteca de interfaz universal en algún grado fuera necesariamente algo bueno. Quizás lo haría, pero en muchos casos una biblioteca de este tipo sería grande y difícil de escribir y mantener, y tal vez incluso lenta. Y si tal bestia, de hecho, no existe para atender el problema particular en cuestión, ¿quién es usted para insistir en que se cree uno, cuando un enfoque de generación de código puede resolver el problema mucho más rápida y fácilmente?
Varias cosas, creo.
Los generadores de código transforman el código escrito en un idioma para codificar en un idioma diferente, generalmente de nivel inferior. Se pregunta, entonces, por qué la gente querría escribir programas usando múltiples idiomas, y especialmente por qué querrían mezclar idiomas de niveles subjetivamente diferentes.
Pero ya toqué eso. Uno elige un lenguaje para una tarea particular basado en parte en su claridad y expresividad para esa tarea. Como el código más pequeño tiene menos errores en promedio y es más fácil de mantener, también existe un sesgo hacia los lenguajes de nivel superior, al menos para el trabajo a gran escala. Pero un programa complejo implica muchas tareas y, a menudo, algunas de ellas pueden abordarse de manera más efectiva en un idioma, mientras que otras se abordan de manera más eficaz o más concisa en otro. Usar la herramienta adecuada para el trabajo a veces significa emplear la generación de código.
fuente
Respondiendo la pregunta dentro del contexto de tu comentario:
Un compilador nunca estará optimizado para su tarea. La razón de esto es simple: está optimizado para hacer muchas tareas. Es una herramienta de uso general utilizada por muchas personas para muchas tareas diferentes. Una vez que sepa cuál es su tarea, puede abordar el código de una manera específica del dominio, haciendo compensaciones que los compiladores no podrían.
Como ejemplo, he trabajado en software en el que un analista puede necesitar escribir algún código. Podrían escribir su algoritmo en C ++ y agregar todas las comprobaciones de límites y trucos de memorización de los que dependen, pero eso requiere saber mucho sobre el funcionamiento interno del código. Prefieren escribir algo simple y dejarme lanzar un algoritmo para generar el código final de C ++. Entonces puedo hacer trucos exóticos para maximizar el rendimiento como el análisis estático que nunca esperaría que soportaran mis analistas. La generación de código les permite escribir de una manera específica del dominio, lo que les permite sacar el producto de la puerta más fácilmente que cualquier herramienta de propósito general.
También he hecho exactamente lo contrario. Tengo otro trabajo que hice que tenía el mandato de "no generar código". Todavía queríamos facilitarles la vida a quienes usan el software, por lo que utilizamos cantidades masivas de metaprogramación de plantillas para hacer que el compilador genere el código sobre la marcha. Por lo tanto, solo necesitaba el lenguaje C ++ de propósito general para hacer mi trabajo.
Sin embargo, hay una trampa. Fue tremendamente difícil garantizar que los errores fueran legibles. Si alguna vez ha usado código metaprogramado de plantilla anteriormente, sabe que un solo error inocente puede generar un error que requiere 100 líneas de nombres de clase incomprensibles y argumentos de plantilla para comprender qué salió mal. Este efecto fue tan pronunciado que el proceso de depuración recomendado para errores de sintaxis fue "Desplácese por el registro de errores hasta que vea la primera vez que uno de sus propios archivos tiene un error. Vaya a esa línea y solo bíjela hasta que se dé cuenta de lo que hizo mal ".
Si hubiéramos utilizado la generación de código, podríamos haber tenido capacidades de manejo de errores mucho más poderosas, con errores legibles por humanos. Así es la vida.
fuente
Hay algunas formas diferentes de usar la generación de código. Podrían dividirse en tres grupos principales:
Supongo que estás hablando del tercer tipo de código generado, ya que esta es la forma más controvertida. En las dos primeras formas, el código generado es un paso intermedio que está muy limpio del código fuente. Pero en la tercera forma no hay una separación formal entre el código fuente y el código generado, excepto que el código generado probablemente tenga un comentario que diga "no edite este código". Todavía abre el riesgo de que los desarrolladores editen el código generado que sería realmente feo. Desde el punto de vista del compilador, el código generado es el código fuente.
Sin embargo, tales formas de código generado pueden ser realmente útiles en un lenguaje de tipo estático. Por ejemplo, cuando se integra con entidades ORM, es realmente útil tener contenedores fuertemente tipados para las tablas de la base de datos. Seguro que podría manejar la integración dinámicamente en tiempo de ejecución, pero perdería seguridad de tipo y soporte de herramientas (finalización de código). Una ventaja importante del lenguaje de tipos estático es el soporte del sistema de tipos en el tipo de escritura en lugar de solo en tiempo de ejecución. (Por el contrario, este tipo de generación de código no es muy frecuente en los idiomas de tipo dinámico, ya que en dicho lenguaje no proporciona ningún beneficio en comparación con las conversiones en tiempo de ejecución).
Debido a que la seguridad de tipos y la finalización del código son características que desea en el momento de la compilación (y al escribir código en un IDE), pero las funciones regulares solo se ejecutan en tiempo de ejecución.
Sin embargo, puede haber un punto medio: F # admite el concepto de proveedores de tipos, que básicamente son interfaces fuertemente tipadas generadas mediante programación en tiempo de compilación. Este concepto probablemente podría reemplazar muchos usos de la generación de código y proporcionar una separación más clara de las preocupaciones.
fuente
Los conjuntos de instrucciones del procesador son fundamentalmente imprescindibles , pero los lenguajes de programación pueden ser declarativos . Ejecutar un programa escrito en un lenguaje declarativo inevitablemente requiere algún tipo de generación de código. Como se menciona en esta respuesta y en otras, una de las principales razones para generar código fuente en un lenguaje legible por humanos es aprovechar las sofisticadas optimizaciones realizadas por los compiladores.
fuente
Lo entendiste al revés. Debería leer
Si algo se puede alimentar a un generador para interpretables , entonces esa cosa es código, no datos.
Es el formato fuente para esa etapa de compilación, y el formato sumidero sigue siendo código.
fuente
gcc -fverbose-asm -O -S
no es el código fuente (y no es solo o principalmente datos), incluso si se trata de alguna forma textual siempre alimentada a GNUas
y a veces leída por humanos.