Interprété vs compilé: une distinction utile?

29

Beaucoup de questions sont posées ici sur les outils de langage interprétés vs compilés. Je me demande si la distinction a vraiment un sens. (En fait, les questions portent généralement sur les langues, mais elles pensent vraiment aux implémentations les plus populaires de ces langues).

Aujourd'hui, pratiquement aucune mise en œuvre n'est strictement interprétée. c'est-à-dire que pratiquement personne n'analyse et exécute le code une ligne à la fois. De plus, l'implémentation qui compile en code machine devient également moins courante. De plus en plus, les compilateurs ciblent une sorte de machine virtuelle.

En fait, la plupart des mises en œuvre convergent vers la même stratégie de base. Le compilateur produit un bytecode qui est interprété ou compilé en code natif via un JIT. C'est vraiment un mélange des idées traditionnelles de compilation et d'interprétation.

Je pose donc la question suivante: existe-t-il une distinction utile entre les implémentations interprétées et l'implémentation compilée de nos jours?

Winston Ewert
la source
7
@DeadMG Pas aussi nouveau que vous ne le pensez: une brève histoire de just-in-time ...
yannis
4
@DeadMG Étant donné que la majorité des nouveaux langages introduits au cours des 10 dernières années fonctionnent principalement sur une sorte de machine virtuelle, je dirais qu'il a raison. Bien sûr, il existe encore (et il y aura des décennies à venir) des langages compilés en code natif, et un JIT restera luxueux (ou pas, si les gars de PyPy ont leur chemin). Donc oui, surestimation possible, mais je conviens que le courant dominant (pour l'instant et l'avenir prévisible) semble être un compilateur de bytecode + éventuellement JIT.
4
@DeadMG, vous devez avoir une longue barbe blanche, si le modèle VM est "nouveau" pour vous. P-codeavait été introduit en 1966 pour la première fois. IBM Aix existe depuis 1986.
SK-logic
6
Des choses comme les coquilles Unix, Tcl et similaires seraient toujours purement interprétées, donc la distinction est logique, au moins dans un CS académique. Mais il est vrai que lorsque les codeurs marmonnent entre interprètes et compilateurs, ils n'ont aucun sens dans la plupart des cas.
SK-logic
3
@ SK-logic, je pense que votre commentaire est une meilleure réponse que n'importe laquelle des réponses réellement publiées
Winston Ewert

Réponses:

23

Il est important de se rappeler que l'interprétation et la compilation ne sont pas seulement des alternatives l'une à l'autre. En fin de compte, tout programme que vous écrivez (y compris un programme compilé en code machine) est interprété. Interpréter le code signifie simplement prendre un ensemble d'instructions et renvoyer une réponse.

Compiler, d'autre part, signifie convertir un programme dans une langue vers une autre langue. Habituellement, on suppose que lors de la compilation, le code est compilé dans un langage de "niveau inférieur" (par exemple, code machine, une sorte de bytecode VM, etc.). Ce code compilé est toujours interprété plus tard.

En ce qui concerne votre question de savoir s'il existe une distinction utile entre les langues interprétées et compilées, mon opinion personnelle est que tout le monde devrait avoir une compréhension de base de ce qui se passe avec le code qu'ils écrivent pendant l'interprétation. Donc, si leur code est compilé en JIT, ou mis en cache par bytecode, etc., le programmeur devrait au moins avoir une compréhension de base de ce que cela signifie.

Zach Smith
la source
3
Oui, le programmeur doit avoir une compréhension de base. Mais je me demande si la terminologie compilée / interprétée ne fait pas obstacle à cela.
Winston Ewert
2
Merci!! Interprétée n'est qu'un synonyme de "exécuté", et c'est ainsi que tous les programmes sont exécutés.
gardenhead
9

La distinction est profondément significative car les langages compilés restreignent la sémantique d'une manière que les langages interprétés ne font pas nécessairement. Certaines techniques d'interprétation sont très difficiles (pratiquement impossibles) à compiler.

Le code interprété peut faire des choses comme générer du code au moment de l'exécution et donner à ce code une visibilité sur les liaisons lexicales d'une étendue existante. Voilà un exemple. Un autre est que les interprètes peuvent être étendus avec du code interprété qui peut contrôler la façon dont le code est évalué. C'est la base des anciens "fexprs" de Lisp: des fonctions qui sont appelées avec des arguments non évalués et qui décident quoi en faire (avoir un accès complet à l'environnement nécessaire pour parcourir le code et évaluer les variables, etc.). Dans les langages compilés, vous ne pouvez pas vraiment utiliser cette technique; vous utilisez des macros à la place: des fonctions qui sont appelées au moment de la compilation avec des arguments non évalués, et traduisez le code plutôt que d'interpréter.

Certaines implémentations de langage sont construites autour de ces techniques; leurs auteurs rejettent la compilation comme étant un objectif important et adoptent plutôt ce type de flexibilité.

L'interprétation sera toujours utile comme technique d'amorçage d'un compilateur. Pour un exemple concret, regardez CLISP (une implémentation populaire de Common Lisp). CLISP a un compilateur qui est écrit en lui-même. Lorsque vous générez CLISP, ce compilateur est interprété au cours des premières étapes de génération. Il est utilisé pour se compiler, puis une fois compilé, la compilation se fait à l'aide du compilateur compilé.

Sans noyau d'interpréteur, vous auriez besoin de démarrer avec du Lisp existant, comme le fait SBCL.

Avec l'interprétation, vous pouvez développer un langage à partir de zéro, en commençant par le langage d'assemblage. Développez les E / S de base et les routines de base, puis écrivez un eval, toujours un langage machine. Une fois que vous avez évalué, écrivez dans la langue de haut niveau; le noyau du code machine effectue l'évaluation. Utilisez cette fonction pour étendre la bibliothèque avec de nombreuses autres routines et écrire également un compilateur. Utilisez le compilateur pour compiler ces routines et le compilateur lui-même.

Interprétation: un tremplin important dans le chemin menant à la compilation!

Kaz
la source
1
OMI, c'est la meilleure réponse. Je travaille sur mon propre langage de jouet et le dernier paragraphe décrit la façon dont je le développe. Cela facilite vraiment le travail sur de nouvelles idées. +1 également pour mentionner le processus d'amorçage CLISP.
sinan
En théorie, tout langage «interprété» peut être transformé en langage «compilé» en générant un fichier EXE composé de l'interpréteur plus le code source ou le bytecode du programme interprété. Peut-être pas très efficace.
dan04
Découvrez comment Wirth et al ont inventé le code P pour simplifier le portage de PASCAL vers de nouvelles architectures de machines. C'était au début des années 1970.
John R. Strohm du
1
Je soupçonne que votre premier paragraphe confond la compilation et l'interprétation avec un comportement statique et dynamique, mais je vais vous donner le bénéfice du doute et demander juste un exemple d'un langage avec une sémantique qui est "pratiquement impossible" à compiler. En ce qui concerne l'amorçage d'un compilateur, il est vrai que la première implémentation doit être écrite dans autre chose que la langue que vous implémentez, mais elle ne doit pas nécessairement être un interpréteur, il peut s'agir d'un compilateur écrit dans une autre langue.
8bittree
1

En fait, de nombreuses implémentations de langues sont toujours strictement interprétées, vous ne les connaissez peut-être pas. Pour n'en nommer que quelques-uns: les langages shell UNIX, les shells Windows cmd et PowerScript, Perl, awk, sed, MATLAB, Mathematica et ainsi de suite.

Charles E. Grant
la source
3
Je crois que Perl est compilé en interne en bytecode, et au moins Mathematica peut être compilé. Et rien ne dicte l'implémentation de awk et sed (je crois que certains des coreutils GNU compilent des expressions régulières en automates finis avant exécution, ce qui les placerait sans doute dans la catégorie «compiler en représentation intermédiaire, interpréter cette catégorie»).
1
En fait, je suis assez sûr que Perl, MATLAB, Mathematica compilent tous en bytecode. Je ne connais pas PowerScript, tu veux dire Powershell? Si c'est le cas, celui qui utilise le CLR et donc utilise le bytecode.
Winston Ewert
@WinstonEwert, désolé, je voulais dire PowerShell. Ma compréhension est que la traduction dans une forme intermédiaire ne signifie pas que quelque chose n'est pas interprété. Heck, l'interprète original de Dartmouth BASIC a traduit la source en jetons avant d'interpréter. Chacun des outils que j'ai mentionnés a un mode où il 1) lit une ligne de source, 2) traduit cette ligne en une forme exécutable (peut-être du code intermédiaire plutôt que du code natif), 3) exécute le code de cette ligne, 4) boucle de retour à 1). Cela correspond à ma compréhension d'un interprète.
Charles E. Grant
2
Le bytecode implique une compilation. Un compilateur de bytecode est simplement un programme qui prend la source et la convertit en bytecode. Par conséquent, toutes les utilisations de bytecode doivent impliquer un compilateur de bytecode. Mais le bytecode doit également être interprété (ou JITted). Donc, tout ce qui utilise le bytecode est un hybride interprète / compilateur.
Winston Ewert
4
Vraiment, mon truc, c'est que les gens lancent des déclarations comme "python est interprété" et "Java est compilé" sans comprendre les implémentations. Je me demande s'il est même utile de décrire une implémentation en ces termes. La vérité est généralement plus compliquée, et essayer de la résumer à interprétée / compilée n'est pas utile.
Winston Ewert
1

Je pense: absolument oui .

En fait, la plupart des mises en œuvre convergent vers la même stratégie de base

Vraiment, C ++ vise à porter au domaine du compilateur un concept de haut niveau qui est généralement remis aux interprètes, mais il reste du côté minoritaire ...

CapelliC
la source
2
Attendez que Clang + LLVM devienne la chaîne d'outils de compilation la plus populaire.
SK-logic
@ SK-logic, malgré son nom, je crois que Clang + LLVM produit du code natif.
Winston Ewert
1
@Winston Ewert, uniquement si vous le souhaitez. Vous pouvez vous arrêter au niveau LLVM IR et faire tout ce que vous voulez ensuite - l'interpréter, le compiler en JIT, l'instrumenter comme vous le souhaitez. Vous pouvez même le traduire en Javascript, puis passer par un interprète: github.com/kripken/emscripten/wiki
SK-logic
@ SK-logic, trucs sympas! Je ne savais pas que LLVM pouvait faire ça.
Winston Ewert
1
La beauté de llvm est cette séparation délibérée entre l'avant et l'arrière. Et les outils pour manipuler le milieu avant de cibler le jeu d'instructions. Vous pouvez fusionner l'intégralité de votre projet en bytecode, puis l'optimiser sur l'ensemble, avec d'autres compilateurs, vous devez avoir une source de fichier unique ou au moins une qui inclut son chemin dans l'arborescence source afin que le compilateur agisse sur une source combinée. De plus, un ensemble d'outils sous llvm est générique pour toutes les cibles, vous n'avez pas à créer pour chaque cible, un compilateur convient à tous (au moins à l'asm de la cible).
old_timer
-1

Distinction utile: les programmes interprétés peuvent se modifier eux-mêmes en ajoutant ou en modifiant des fonctions lors de l'exécution.

Diego Pacheco
la source
8
Absurdité. Le code (machine) auto-modifiable est la plus ancienne astuce du livre. Là encore, certains soutiennent que même le code natif est finalement interprété par un interprète moulé dans du silicium (le CPU). Mais si nous supposons cela, tout le code est interprété et il n'y a aucune distinction à faire.
2
@delnan a raison. J'ajouterai simplement que les langages modernes peuvent se modifier en créant dynamiquement de nouvelles classes et en chargeant / déchargeant des bibliothèques (ou des "assemblys" dans .NET par exemple)
Jalayn
5
Common Lisp est compilé, mais vous pouvez toujours remplacer facilement les définitions de fonction dans le runtime.
SK-logic
C'est une caractéristique d'interprétation vraiment intéressante et nécessaire (par exemple dans Prolog).
CapelliC