La distinción entre código interpretado y compilado es probablemente una ficción, como lo subraya el comentario de Raphael :
the claim seems to be trivially wrong without further assumptions: if there is
an interpreter, I can always bundle interpreter and code in one executable ...
El hecho es que el código siempre se interpreta, por software, por hardware o una combinación de ambos, y el proceso de compilación no puede determinar cuál será.
Lo que percibes como compilación es un proceso de traducción de un idioma (para el origen) a otro idioma (para el destino). Y, el intérprete de suele ser diferente de la intérprete para .T S TSTST
El programa compilado se traduce de una forma sintáctica a otra forma sintáctica , de modo que, dada la semántica prevista de los lenguajes y , y tienen el mismo comportamiento computacional, hasta algunas cosas que generalmente intenta cambiar, posiblemente para optimizar, como la complejidad o la eficiencia simple (tiempo, espacio, superficie, consumo de energía). Estoy tratando de no hablar de equivalencia funcional, ya que requeriría definiciones precisas.P T S T P S P TPSPTSTPSPT
Algunos compiladores se han utilizado simplemente para reducir el tamaño del código, no para "mejorar" la ejecución. Este fue el caso del lenguaje utilizado en el sistema Platón (aunque no lo llamaron compilación).
Usted puede considerar plenamente su código compilado si, después del proceso de compilación, ya no es necesario el intérprete de . Al menos, esa es la única forma en que puedo leer su pregunta, como una pregunta de ingeniería más que teórica (ya que, en teoría, siempre puedo reconstruir el intérprete).S
Una cosa que puede plantear problemas, afaik, es la meta-circularidad . Es entonces cuando un programa manipulará estructuras sintácticas en su propio lenguaje fuente , creando fragmentos de programa que luego se interpretarán como si hubieran sido parte del programa original. Como puede producir fragmentos de programa arbitrarios en el lenguaje como resultado de la computación arbitraria manipulando fragmentos sintácticos sin sentido, supongo que puede hacer que sea casi imposible (desde un punto de vista de ingeniería) compilar el programa en el lenguaje , de modo que ahora generar fragmentos de . Por lo tanto , se necesitará el intérprete para , o al menos el compilador de aS T T S S T SSSTTSST para compilar sobre la marcha los fragmentos generados en (ver también este documento ).S
Pero no estoy seguro de cómo se puede formalizar esto correctamente (y no tengo tiempo en este momento). E imposible es una gran palabra para un problema que no está formalizado.
Observaciones posteriores
Agregado después de 36 horas. Es posible que desee omitir esta larga secuela.
Los numerosos comentarios a esta pregunta muestran dos puntos de vista sobre el problema: un punto de vista teórico que lo considera sin sentido, y un punto de vista de la ingeniería que desafortunadamente no se formaliza tan fácilmente.
Hay muchas formas de ver la interpretación y la compilación, y trataré de esbozar algunas. Intentaré ser tan informal como pueda
El diagrama de lápida
Una de las primeras formalizaciones (principios de la década de 1960 hasta finales de 1990) son los diagramas T o
Tombstone . Estos diagramas presentan en elementos gráficos componibles el lenguaje de implementación del intérprete o compilador, el idioma de origen que se interpreta o compila y el idioma de destino en el caso de los compiladores. Las versiones más elaboradas pueden agregar atributos. Estas representaciones gráficas pueden verse como axiomas, reglas de inferencia, utilizables para derivar mecánicamente la generación del procesador a partir de una prueba de su existencia a partir de los axiomas, a la Curry-Howard (aunque no estoy seguro de que se hiciera en los años sesenta :).
Evaluación parcial
Otra visión interesante es el paradigma de evaluación parcial . Estoy tomando una vista simple de los programas como un tipo de implementación de funciones que calcula una respuesta dados algunos datos de entrada. A continuación, un intérprete
para el lenguaje es un programa que tome un programa
escritos en y datos para ese programa, y calcula el resultado de acuerdo con la semántica de . La evaluación parcial es una técnica para especializar un programa de dos argumentos y , cuando solo se conoce un argumento, digamos . La intención es tener una evaluación más rápida cuando finalmente obtenga el segundo argumento S p S S d S a 1 a 2 a 1 a 2 a 2 a 1 a 1 a 2ISSpSSdSa1a2a1a2 . Es especialmente útil si cambia con más frecuencia que ya que el costo de la evaluación parcial con se puede amortizar en todos los cálculos donde solo está cambiando .a2a1a1a2
Esta es una situación frecuente en el diseño de algoritmos (a menudo el tema del primer comentario sobre SE-CS), cuando alguna parte más estática de los datos se procesa previamente, de modo que el costo del procesamiento previo se puede amortizar en todas las aplicaciones del algoritmo con partes más variables de los datos de entrada.
Esta es también la situación de los intérpretes, ya que el primer argumento es el programa que se ejecutará, y generalmente se ejecuta muchas veces con datos diferentes (o tiene subpartes ejecutadas muchas veces con datos diferentes). Por lo tanto, se convierte en una idea natural especializar a un intérprete para una evaluación más rápida de un programa dado al evaluarlo parcialmente en este programa como primer argumento. Esto puede verse como una forma de compilar el programa, y ha habido un importante trabajo de investigación sobre la compilación mediante la evaluación parcial de un intérprete en su primer argumento (del programa).
El teorema de Smn
Lo bueno del enfoque de evaluación parcial es que tiene sus raíces en la teoría (aunque la teoría puede ser mentirosa), especialmente en
el teorema de Smn de Kleene . Estoy tratando de dar una presentación intuitiva, esperando que no moleste a los teóricos puros.
Dada una numeración de Gödel de funciones recursivas, puede ver como su hardware, de modo que dado el número de Gödel
(leer el código de objeto ) de un programa es la función definida por (es decir, calculada por el código de objeto en tu hardware)φφpφpp
En su forma más simple, el teorema se establece en wikipedia de la siguiente manera (hasta un pequeño cambio en la notación):
Dada una numeración de Gödel de funciones recursivas, hay una función recursiva primitiva de dos argumentos con la siguiente propiedad: por cada número de Gödel de una función computable parcial con dos argumentos, las expresiones y se definen para las mismas combinaciones de números naturales y , y sus valores son iguales para cualquier tal combinación. En otras palabras, la siguiente igualdad extensional de funciones es válida para cada :
φσqfφσ(q,x)(y)f(x,y)xyxφσ(q,x)≃λy.φq(x,y).
Ahora, tomando como el intérprete , como el código fuente de un programa , e como los datos para ese programa, podemos escribir:
qISxpSydφσ(IS,pS)≃λd.φIS(pS,d).
φIS puede ser visto como la ejecución del intérprete
en el hardware, es decir, como un negro-caja lista para interpretar los programas escritos en un lenguaje .ISS
La función puede verse como una función que especializa al intérprete para el programa , como en la evaluación parcial. Por lo tanto, se puede ver que el número de Gödel tiene un código de objeto que es la versión compilada del programa .σISPSσ(IS,pS)pS
Entonces, la función puede verse como una función que toma como argumento el código fuente de un programa
escrito en el lenguaje , y devuelve la versión del código objeto para ese programa Entonces, es lo que generalmente se llama un compilador.CS=λqS.σ((IS,qS)qSSCS
Algunas conclusiones
Sin embargo, como dije: "la teoría puede ser mentirosa", o en realidad parece serlo. El problema es que no sabemos nada de la función . En realidad, hay muchas funciones de este tipo, y supongo que la prueba del teorema puede usar una definición muy simple, que podría no ser mejor, desde un punto de vista de ingeniería, que la solución propuesta por Rafael: simplemente agrupar el código fuente con el intérprete . Esto siempre se puede hacer, por lo que podemos decir: la compilación siempre es posible.q S I SσqSIS
Formalizar una noción más restrictiva de lo que es un compilador requeriría un enfoque teórico más sutil. No sé qué se pudo haber hecho en esa dirección. El trabajo muy real realizado en la evaluación parcial es más realista desde el punto de vista de la ingeniería. Y, por supuesto, existen otras técnicas para escribir compiladores, incluida la extracción de programas a partir de la prueba de su especificación, desarrollada en el contexto de la teoría de tipos, basada en el isomorfismo de Curry-Howard (pero me estoy saliendo de mi dominio de competencia) .
Mi propósito aquí ha sido mostrar que el comentario de Raphael no es "loco", sino un recordatorio sensato de que las cosas no son obvias, ni siquiera simples. Decir que algo es imposible es una afirmación sólida que requiere definiciones precisas y una prueba, aunque solo sea para tener una comprensión precisa de cómo y por qué es imposible . Pero construir una formalización adecuada para expresar tal prueba puede ser bastante difícil.
Dicho esto, incluso si una característica específica no es compilable, en el sentido que entienden los ingenieros, las técnicas de compilación estándar siempre se pueden aplicar a partes de los programas que no usan dicha característica, como lo señala la respuesta de Gilles.
Para seguir las observaciones clave de Gilles de que, dependiendo del idioma, se puede hacer algo en tiempo de compilación, mientras que otro se debe hacer en tiempo de ejecución, lo que requiere un código específico, podemos ver que el concepto de compilación es realmente mal definido, y probablemente no sea definible de manera satisfactoria. La compilación es solo un proceso de optimización, como intenté mostrar en la sección de evaluación parcial , cuando lo comparé con el preprocesamiento de datos estáticos en algunos algoritmos.
Como un proceso de optimización complejo, el concepto de compilación en realidad pertenece a un continuo. Dependiendo de la característica del idioma o del programa, cierta información puede estar disponible estáticamente y permitir una mejor optimización. Otras cosas tienen que posponerse al tiempo de ejecución. Cuando las cosas se ponen realmente mal, todo debe hacerse en tiempo de ejecución, al menos para algunas partes del programa, y agrupar el código fuente con el intérprete es todo lo que puede hacer. Por lo tanto, este paquete es solo el extremo inferior de este continuo de compilación. Gran parte de la investigación sobre compiladores trata de encontrar formas de hacer estáticamente lo que solía hacerse dinámicamente. La recolección de basura en tiempo de compilación parece un buen ejemplo.
Tenga en cuenta que decir que el proceso de compilación debería producir código de máquina no ayuda. Eso es precisamente lo que puede hacer la agrupación ya que el intérprete es código de máquina (bueno, la cosa puede volverse un poco más compleja con la compilación cruzada).