La diferencia entre un compilador y un intérprete
On diciembre 31, 2021 by adminSegún sus definiciones, la diferencia entre un compilador y un intérprete parece bastante clara:
- El intérprete es un programa que ejecuta directamente las instrucciones escritas en un lenguaje de programación
- El compilador es un programa que transforma el código fuente en un lenguaje de bajo(er)-nivel
Sin embargo, si se profundiza, se encuentra cierta confusión entre ambos.
De hecho, un intérprete podría traducir el lenguaje fuente de forma intermedia, para acelerar la ejecución. Eso es lo que suele ocurrir con un lenguaje que se apoya en una máquina virtual. Esto naturalmente lleva a las siguientes preguntas:
¿Todos los lenguajes que utilizan una máquina virtual son interpretados?
¿Son todos realmente compilados?
Podría decirse que ambas cosas: un lenguaje es primero compilado en una forma/lenguaje intermedio y luego esta cosa intermedia es interpretada en tiempo de ejecución. Lo que también nos lleva a otra cuestión, un compilador y un intérprete no deben pensarse como un solo programa, sino más bien como un grupo de programas, un sistema. Lo que usted, como usuario, piensa que es un compilador puede en realidad incluir más de un programa. Por ejemplo, puede incluir un enlazador: un programa que combina diferentes archivos de objetos en un solo archivo, para que pueda ser utilizado más fácilmente. Algo similar podría decirse de un intérprete.
¿Puedes decirme todo sobre los compiladores &interpretes?
Entonces, ¿cuáles son todas las piezas que componen un compilador o un intérprete? Podrías buscar una respuesta precisa y técnica a estas preguntas en el mundo académico. O puedes encontrar discusiones sobre estas cuestiones en StackOverflow.
Lo que realmente nos importa como desarrolladores, o incluso como creadores de un lenguaje, es cuáles son las diferencias al trabajar con ellos. Ambos tienen ventajas y desventajas, y de hecho algunos lenguajes pueden tener tanto un intérprete como un compilador, o más de uno. Eso es lo que vamos a ver.
El punto principal sigue en pie: un intérprete ejecuta el código ahora, un compilador prepara el código fuente para una ejecución que viene después. Todas las diferencias prácticas descienden de estos diferentes objetivos.
¿Cómo se distribuye un programa
En términos prácticos, una diferencia importante es que un compilador genera un programa autónomo, mientras que un programa interpretado siempre necesita el intérprete para ejecutarse.
Una vez que se tiene un programa compilado se puede ejecutar sin necesidad de instalar nada más. Esto simplifica la distribución. Por otro lado el ejecutable funciona en una plataforma específica: diferentes sistemas operativos y diferentes procesadores necesitan diferentes versiones compiladas. Por ejemplo, un programa C++ compilado podría funcionar en un ordenador con un procesador x86, pero no en uno con un chip ARM. O podría funcionar en un sistema Linux, pero no en uno Windows.
Si vas a interpretar un programa puedes distribuir la misma copia a usuarios de diferentes plataformas. Sin embargo, necesitarán un intérprete que funcione en su plataforma específica. Puedes distribuir el código fuente original o una forma intermedia. Una forma intuitiva de ver un intérprete es la siguiente: es como la función eval
de JavaScript. Funciona dondequiera que funcione JavaScript, pero necesita un intérprete de JavaScript para esa plataforma para ejecutarse.
Soporte multiplataforma
Esta es una diferencia técnica que conlleva importantes consecuencias reales: es más fácil hacer programas multiplataforma con un lenguaje de programación interpretado.
Eso se debe a que, en su mayor parte, sólo estás creando un programa para la plataforma del intérprete. Será el propio intérprete el que lo traduzca a la forma adecuada para la plataforma real (por ejemplo, Windows/Linux y x86/ARM). Por supuesto, siguen existiendo algunas diferencias en cada plataforma de las que debes ser consciente. Un ejemplo común es el carácter separador de directorios.
Cuando compilas un programa, en cambio, tienes que ocuparte tú mismo de todas las pequeñas diferencias entre cada plataforma. Esto sucede en parte porque los lenguajes compilados tienden a ser lenguajes de bajo(er) nivel, como C++, por lo que te dan un menor acceso al sistema y, por tanto, más responsabilidad. Pero otra razón es que todas las bibliotecas que utilizas necesitan por sí mismas soportar diferentes plataformas. Así que, si no soportan Windows, no puedes soportar Windows.
La velocidad tiene múltiples caras
De nuevo, en el caso de la velocidad, tenemos una especie de paradoja: un compilador es a la vez más rápido y más lento que un intérprete. Mucha gente sabe que un programa compilado es mucho más rápido que uno interpretado, pero esto no es todo. Un programa compilado es más rápido de ejecutar que un programa interpretado, pero lleva más tiempo compilar y ejecutar un programa que sólo interpretarlo.
Un compilador produce efectivamente programas más rápidos. Esto ocurre fundamentalmente porque debe analizar cada declaración sólo una vez, mientras que un intérprete debe analizarla cada vez. Además, un compilador puede optimizar el código ejecutable que produce. Esto se debe tanto a que sabe exactamente dónde se va a ejecutar como a que necesita tiempo para optimizar el código. Tiempo que haría la interpretación demasiado lenta.
Velocidad de ejecución frente a velocidad de desarrollo
Podrías pensar que esto es una minucia: si compilas un programa se ejecuta más rápido, el tiempo que se tarda en compilar no importa. Esta suele ser la opinión de la vieja escuela. Y sin duda el programa resultante se ejecuta más veces de lo que se compila. Entonces, ¿a quién le importa que el desarrollo lleve más tiempo? Bueno, seguro que se adopta una actitud de «llevar el dolor» al desarrollo que es algo admirable. Pero ¿qué pasa si las ganancias en el tiempo de ejecución no son relevantes, mientras que las pérdidas en la productividad del desarrollo son significativas?
Una cosa es si estás creando un sistema operativo y otra si estás haciendo una aplicación de selfie. Incluso tus usuarios pueden preferir una pérdida no apreciable en la velocidad de ejecución a cambio de obtener funciones más rápidamente. No hay una respuesta única para todos los casos: en algunos contextos la productividad importa más que la velocidad, y en otros ocurre lo contrario.
Los misterios de la depuración
Hay otro aspecto particular que acaba siendo más incierto de lo que uno podría parecer: la depuración. Sobre el papel la depuración es más fácil cuando se utiliza un intérprete que cuando se utiliza un compilador. Esto es cierto por varias razones:
- con el intérprete hay una sola versión del ejecutable; no se necesita una versión de depuración para el desarrollo y otra de lanzamiento para el usuario final
- hay menos errores específicos de la plataforma utilizando un intérprete
- ya que el intérprete transforma el código sobre la marcha, la información del código fuente sigue estando disponible
- ya que el intérprete ejecuta una sentencia a la vez, es más fácil encontrar un error
La diferencia que marcan las herramientas de desarrollo
Aunque todo esto es cierto en la práctica, podría ser menos relevante de lo que parece. De hecho, si piensa en su experiencia, probablemente encontrará que depurar JavaScript es más difícil que depurar C++. ¿Por qué? En parte es por el diseño de los propios lenguajes. JavaScript utiliza tipado dinámico, mientras que C++ utiliza tipado estático. Esto último facilita la detección temprana de errores. Pero al final todo se reduce a las herramientas de desarrollo. Compilar C++ a mano es difícil, por lo que la mayoría de la gente utiliza IDEs para desarrollar con él. Por otro lado, puedes usar fácilmente un editor de texto y herramientas de línea de comandos para desarrollar en JavaScript.
Esto significa que, en términos prácticos, si desarrollas con C++, también puedes depurar C++. En cambio, puedes desarrollar con JavaScript sin saber cómo hacer una depuración adecuada en JavaScript.
Dicho esto, si los ponemos en el mismo contexto, cada uno con un gran IDE y herramientas de apoyo, la situación vuelve a la normalidad. En efecto, muchos entornos interpretados son utilizados por personas que quieren aprender a utilizar un nuevo lenguaje. Es más fácil probar, y encontrar lo que está bien y lo que está mal, mirando lo que sucede línea por línea y en tiempo real.
Resumen
Hemos visto las principales diferencias que importan entre un compilador y un intérprete. Y lo que es más importante, hemos visto que las consecuencias de las diferentes filosofías son más importantes que las técnicas. En definitiva, hay culturas que vienen acompañadas de ciertas opciones técnicas que acaban siendo relevantes por sí mismas. Si quieres velocidad y facilidad de desarrollo vas a elegir todas las tecnologías para la velocidad y no sólo una. Y tus usuarios van a seguir tu ejemplo.
Este es un aspecto crucial para pensar, especialmente si quieres crear tu propio lenguaje de programación. Rasmus Lerdorf creó PHP para que fuera fácil de usar. Y de hecho era increíblemente más fácil de usar en comparación con las alternativas, al menos en el momento de su creación. Pero empezó más como una biblioteca que como un lenguaje. Y aunque ha mejorado mucho, todavía sufre de sus inicios. Todavía se puede crear buen código PHP, pero lo hacen menos usuarios de lo habitual. Porque si sólo necesitas algo que funcione, la seguridad, el mantenimiento, etc., todo lo demás viene después.
Si quieres saber cómo puedes construir prácticamente un intérprete o un compilador para tu lenguaje, puedes echar un vistazo a los recursos para crear un lenguaje de programación. Si quieres aprender eso, y todo lo que necesitas para crear tu propio lenguaje, sólo tienes que elegir un gran libro, amado por niños y adultos por igual, sobre cómo crear lenguajes pragmáticos y ligeros.
5 cosas que hay que hacer bien al construir un lenguaje
Reciba la lista de comprobación por correo electrónico y obtenga más consejos sobre la construcción de lenguajes
Deja una respuesta