Interpretado vs Compilado: ¿Una distinción útil?

29

Aquí se hacen muchas preguntas sobre implementos de lenguaje interpretados o compilados. Me pregunto si la distinción realmente tiene sentido. (En realidad, las preguntas son generalmente sobre idiomas, pero realmente están pensando en las implementaciones más populares de esos idiomas).

Hoy casi ninguna implementación se interpreta estrictamente. es decir, casi nadie analiza y ejecuta el código línea por línea. Además, la implementación que compila el código de máquina también se está volviendo menos común. Cada vez más, los compiladores apuntan a algún tipo de máquina virtual.

De hecho, la mayoría de las implementaciones convergen en la misma estrategia básica. El compilador produce bytecode que se interpreta o compila en código nativo a través de un JIT. Es realmente una mezcla de las ideas tradicionales de compilación e interpretación.

Por lo tanto, pregunto: ¿Hay alguna distinción útil entre las implementaciones interpretadas y la implementación compilada en estos días?

Winston Ewert
fuente
77
@DeadMG No es tan nueva como puede pensar: Una historia breve de solo-en-tiempo ...
Yannis
44
@DeadMG Dado que la mayoría de los nuevos idiomas introducidos en los últimos 10 años más o menos se ejecutan principalmente en algún tipo de VM, diría que tiene razón. Por supuesto, todavía hay (y habrá en las próximas décadas) idiomas compilados en código nativo, y un JIT seguirá siendo un lujo (o no, si los chicos de PyPy se salen con la suya). Entonces, sí, posible exageración, pero estoy de acuerdo en que la corriente principal (por ahora y el futuro previsible) parece ser el compilador de código de bytes + posiblemente JIT.
44
@DeadMG, debe tener una larga barba blanca, si el modelo VM es "nuevo" para usted. P-codehabía sido introducido en 1966 primero. IBM Aix existe desde 1986.
SK-logic
66
Cosas como shells de Unix, Tcl y similares siempre se interpretarían puramente, por lo que la distinción tiene sentido, al menos en un CS académico. Pero es cierto que cuando los codificadores murmuran sobre intérpretes frente a compiladores, no tienen ningún sentido en la mayoría de los casos.
SK-logic
3
@ SK-logic, creo que su comentario es una mejor respuesta que cualquiera de las respuestas realmente publicadas
Winston Ewert

Respuestas:

23

Es importante recordar que interpretar y compilar no son solo alternativas entre sí. Al final, cualquier programa que escriba (incluido uno compilado en código máquina) se interpreta. Interpretar el código simplemente significa tomar un conjunto de instrucciones y devolver una respuesta.

Compilar, por otro lado, significa convertir un programa en un idioma a otro idioma. Por lo general, se supone que cuando se realiza la compilación, el código se compila en un lenguaje de "nivel inferior" (por ejemplo, código de máquina, algún tipo de código de bytes de VM, etc.). Este código compilado todavía se interpreta más adelante.

Con respecto a su pregunta de si existe una distinción útil entre los idiomas interpretados y compilados, mi opinión personal es que todos deben tener una comprensión básica de lo que está sucediendo con el código que escriben durante la interpretación. Entonces, si su código se está compilando JIT, o se almacena en caché por bytecode, etc., el programador debe al menos tener una comprensión básica de lo que eso significa.

Zach Smith
fuente
3
Sí, el programador debe tener una comprensión básica. Pero me pregunto si la terminología compilada / interpretada no se interpone en el camino de eso.
Winston Ewert
2
¡¡Gracias!! Interpretado es solo un sinónimo de "ejecutado", y así es como se ejecutan todos los programas.
cabeza de jardín
9

La distinción es profundamente significativa porque los lenguajes compilados restringen la semántica de formas que los lenguajes interpretados no necesariamente. Algunas técnicas interpretativas son muy difíciles (prácticamente imposibles) de compilar.

El código interpretado puede hacer cosas como generar código en tiempo de ejecución y darle visibilidad a ese código en enlaces léxicos de un alcance existente. Ese es un ejemplo. Otra es que los intérpretes se pueden ampliar con código interpretado que puede controlar cómo se evalúa el código. Esta es la base de los antiguos "fexprs" de Lisp: funciones que se llaman con argumentos no evaluados y deciden qué hacer con ellos (tener acceso total al entorno necesario para recorrer el código y evaluar variables, etc.). En lenguajes compilados, realmente no puedes usar esa técnica; usa macros en su lugar: funciones que se llaman en tiempo de compilación con argumentos no evaluados y traducen el código en lugar de interpretarlo.

Algunas implementaciones de lenguaje se basan en estas técnicas; sus autores rechazan la compilación como un objetivo importante, y más bien adoptan este tipo de flexibilidad.

La interpretación siempre será útil como técnica para arrancar un compilador. Para un ejemplo concreto, mire CLISP (una implementación popular de Common Lisp). CLISP tiene un compilador que está escrito en sí mismo. Cuando compila CLISP, ese compilador se interpreta durante los primeros pasos de construcción. Se utiliza para compilarse, y luego, una vez que se compila, la compilación se realiza utilizando el compilador compilado.

Sin un núcleo de intérprete, necesitaría arrancar con algunos Lisp existentes, como lo hace SBCL.

Con la interpretación, puede desarrollar un lenguaje desde cero, comenzando con el lenguaje ensamblador. Desarrolle las rutinas básicas de E / S y núcleo, luego escriba una evaluación, aún lenguaje máquina Una vez que haya evaluado, escriba en el lenguaje de alto nivel; el núcleo del código de máquina realiza la evaluación. Utilice esta función para ampliar la biblioteca con muchas más rutinas y también escriba un compilador. Use el compilador para compilar esas rutinas y el compilador mismo.

Interpretación: ¡un trampolín importante en el camino que conduce a la compilación!

Kaz
fuente
1
OMI, esta es la mejor respuesta. Estoy trabajando en mi propio lenguaje de juguetes y el último párrafo describe la forma en que lo estoy desarrollando. Realmente hace que trabajar en nuevas ideas sea mucho más fácil. También +1 por mencionar el proceso de arranque de CLISP.
sinan
En teoría, cualquier lenguaje "interpretado" puede convertirse en uno "compilado" generando un archivo EXE que consista en el intérprete más el código fuente o código de bytes para el programa interpretado. Sin embargo, podría no ser muy eficiente.
dan04
Lea sobre cómo Wirth et al inventaron el código P para simplificar la transferencia de PASCAL a nuevas arquitecturas de máquinas. Eso fue a principios de la década de 1970.
John R. Strohm
1
Sospecho que su párrafo inicial es una compilación e interpretación confusas con un comportamiento estático y dinámico, pero le daré el beneficio de la duda y le pediré un ejemplo de un lenguaje con semántica que es "prácticamente imposible" compilar. Con respecto al arranque de un compilador, es cierto que la primera implementación debe escribirse en algo que no sea el idioma que está implementando, pero no tiene que ser un intérprete, podría ser un compilador escrito en otro idioma.
8bittree
1

En realidad, muchas implementaciones de idiomas todavía se interpretan estrictamente, es posible que no se dé cuenta de ellas. Por nombrar algunos: los lenguajes de shell de UNIX, los cmd de Windows y los shells de PowerScript, Perl, awk, sed, MATLAB, Mathematica, etc.

Charles E. Grant
fuente
3
Creo que Perl está compilado internamente en bytecode, y al menos Mathematica puede compilarse. Y nada dicta la implementación de awk y sed (creo que algunos de los coreutils de GNU compilan expresiones regulares para autómatas finitos antes de la ejecución, lo que podría ubicarlos en la categoría "compilar a representación intermedia, interpretar esa").
1
En realidad, estoy bastante seguro de que Perl, MATLAB, Mathematica compilan todos en bytecode. No estoy familiarizado con PowerScript, ¿te refieres a Powershell? Si es así, usa CLR y también usa bytecode.
Winston Ewert
@ WinstonEwert, lo siento, me refería a PowerShell. Según tengo entendido, la traducción a una forma intermedia no significa que algo no se interprete. Heck, el intérprete original de Dartmouth BASIC tradujo la fuente a tokens antes de interpretar. Cada una de las herramientas que mencioné tiene un modo en el que 1) lee una línea de origen, 2) traduce esa línea en una forma ejecutable (posiblemente algún código intermedio en lugar de código nativo), 3) ejecuta el código para esa línea, 4) volver a 1). Eso corresponde a mi comprensión de un intérprete.
Charles E. Grant
2
Bytecode implica compilado. Un compilador de bytecode es simplemente un programa que toma la fuente y la convierte en bytecode. Por lo tanto, todos los usos de bytecode deben involucrar un compilador de bytecode. Pero el bytecode también tiene que ser interpretado (o JITted). Entonces, cualquier cosa que use bytecode es un híbrido de intérprete / compilador.
Winston Ewert
44
En realidad, lo mío es que la gente arroja declaraciones como "se interpreta Python" y "Java se compila" sin comprender las implementaciones. Me pregunto si incluso es útil describir una implementación en esos términos. La verdad generalmente es más complicada, y tratar de reducirlo a interpretado / compilado no es útil.
Winston Ewert
1

Pienso: absolutamente sí .

De hecho, la mayoría de las implementaciones convergen en la misma estrategia básica.

Realmente, C ++ apunta a portar al dominio del compilador algún concepto de alto nivel que generalmente se entrega a los intérpretes, pero se mantiene en el lado minoritario ...

CapelliC
fuente
2
Espere hasta que Clang + LLVM se convierta en la cadena de herramientas de compilación más popular.
SK-logic
@ SK-logic, a pesar del nombre, creo que Clang + LLVM produce código nativo.
Winston Ewert
1
@ Winston Ewert, solo si quieres. Puede detenerse en el nivel LLVM IR y hacer lo que quiera con él: interpretarlo, compilarlo JIT, instrumentarlo de la manera que desee. Incluso puede traducirlo a Javascript y luego pasar por un intérprete: github.com/kripken/emscripten/wiki
SK-logic
@ SK-logic, cosas ordenadas! No sabía que LLVM podría hacer eso.
Winston Ewert
1
La belleza de llvm es esta separación deliberada entre el extremo frontal y el extremo posterior. Y las herramientas para manipular el medio antes de apuntar al conjunto de instrucciones. Puede fusionar todo su proyecto en bytecode y luego optimizarlo en todo, con otros compiladores necesitaría tener un solo origen de archivo o al menos uno que incluya su camino hacia abajo a través del árbol de origen para que el compilador actúe en una fuente combinada. Además, un conjunto de herramientas bajo llvm es genérico para todos los objetivos, no tiene que compilar para cada objetivo, un compilador se adapta a todos (al menos a la asm del objetivo).
old_timer
-1

Distinción útil: los programas interpretados pueden modificarse agregando o cambiando funciones en tiempo de ejecución.

Diego Pacheco
fuente
8
Disparates. El código de auto-modificación (máquina) es el truco más antiguo del libro. Por otra parte, algunos argumentan que incluso el código nativo es interpretado en última instancia por un intérprete convertido en silicio (la CPU). Pero si asumimos eso, todo el código se interpreta y no hay distinción que hacer.
2
@delnan tiene razón. Agregaré que los lenguajes modernos pueden modificarse creando nuevas clases dinámicamente y cargando / descargando bibliotecas (o "ensamblados" en .NET, por ejemplo)
Jalayn
55
Common Lisp se compila, pero aún puede reemplazar fácilmente las definiciones de funciones en tiempo de ejecución.
SK-logic
Esa es una característica de interpretación realmente interesante y necesaria (por ejemplo, en Prolog).
CapelliC