La distinction entre code interprété et compilé est probablement une fiction, comme le souligne Raphaël dans ses commentaires :
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 ...
Le fait est que le code est toujours interprété, par un logiciel, par le matériel ou par une combinaison des deux, et le processus de compilation ne peut pas dire lequel il sera.
Ce que vous percevez comme compilation est un processus de traduction d'une langue (pour la source) vers une autre langue (pour la cible). Et, l'interprète de est généralement différent de l'interprète pour .T S TSTST
Le programme compilé est traduit d’une forme syntaxique à une autre forme syntaxique , de sorte que, compte tenu de la sémantique voulue des langages et , et aient le même comportement informatique, jusqu’à un certain nombre de choses que vous essayez habituellement de faire. changer, éventuellement à optimiser, comme la complexité ou l'efficacité simple (temps, espace, surface, consommation d'énergie). J'essaie de ne pas parler d'équivalence fonctionnelle, car cela exigerait des définitions précises.P T S T P S P TPSPTSTPSPT
Certains compilateurs ont été utilisés simplement pour réduire la taille du code, pas pour "améliorer" l'exécution. C'était le cas du langage utilisé dans le système Plato (bien qu'ils ne l'appelaient pas la compilation).
Vous pouvez envisager votre code entièrement compilé si, après le processus de compilation, vous ne avez plus besoin de l'interprète pour . Du moins, c’est la seule façon pour moi de lire votre question, en tant que question technique plutôt que théorique (puisque, en théorie, je peux toujours reconstruire l’interprète).S
Une chose qui peut poser problème, autant que je sache, est la méta-circularité . C'est à ce moment qu'un programme manipulera des structures syntaxiques dans son propre langage source , créant ainsi des fragments de programme qui seront ensuite interprétés comme s'ils faisaient partie du programme d'origine. Étant donné que vous pouvez produire des fragments de programme arbitraires dans le langage à la suite de calculs arbitraires manipulant des fragments syntaxiques sans signification, je suppose que vous pouvez rendre presque impossible (d’un point de vue technique) de compiler le programme dans le langage , de sorte que il génère maintenant des fragments de . Par conséquent, l’interprète de sera nécessaire, ou du moins le compilateur de àS T T S S T SSSTTSST pour la compilation à la volée des fragments générés en (voir aussi ce document ).S
Mais je ne sais pas comment cela peut être formalisé correctement (et je n’ai pas le temps de le faire pour le moment). Et impossible est un gros mot pour un problème qui n’est pas formalisé.
Autres remarques
Ajouté après 36 heures. Vous voudrez peut-être éviter cette très longue suite.
Les nombreux commentaires sur cette question montrent deux points de vue sur le problème: un point de vue théorique qui le considère comme dépourvu de sens et un point de vue technique qui, malheureusement, n'est pas aussi facilement formalisé.
Il existe de nombreuses manières d’interpréter et de compiler, et j’essaierai d’en esquisser quelques-unes. Je vais essayer d'être aussi informel que possible
Le diagramme de pierre tombale
L'une des premières formalisations (du début des années 1960 à la fin de 1990) concerne les diagrammes en T ou de
Tombstone . Ces diagrammes présentent sous forme d'éléments graphiques composables le langage d'implémentation de l'interpréteur ou du compilateur, le langage source interprété ou compilé et le langage cible dans le cas des compilateurs. Des versions plus élaborées peuvent ajouter des attributs. Ces représentations graphiques peuvent être considérées comme des axiomes, des règles d'inférence, utilisables pour dériver mécaniquement la génération de processeurs d'une preuve de leur existence à partir des axiomes, à la Curry-Howard (bien que je ne sois pas sûr que cela ait été fait dans les années soixante :).
Évaluation partielle
Le paradigme de l’ évaluation partielle est un autre point de vue intéressant . Je considère simplement les programmes comme une sorte d’implémentation de fonction qui calcule une réponse à partir de données d’entrée. Ensuite , un interprète
pour la langue est un programme qui prend un programme
écrit en et les données pour ce programme, et calcule le résultat en fonction de la sémantique de . L'évaluation partielle est une technique permettant de spécialiser un programme de deux arguments et , lorsqu'un seul argument, par exemple , est connu. L'intention est d'avoir une évaluation plus rapide lorsque vous obtenez enfin le deuxième argument S p S S d S a 1 a 2 a 1 a 2 a 2 a 1 a 1 a 2ISSpSSdSa1a2a1a2 . C'est particulièrement utile si change plus souvent que car le coût d'une évaluation partielle avec peut être amorti sur tous les calculs pour lesquels seul est en train de changer.a2a1a1a2
Il s'agit d'une situation fréquente dans la conception d'algorithmes (souvent le sujet du premier commentaire sur SE-CS), lorsqu'une partie plus statique des données est prétraitée, de sorte que le coût du prétraitement puisse être amorti sur toutes les applications. de l'algorithme avec des parties plus variables des données d'entrée.
C'est également la situation même des interprètes, car le premier argument est le programme à exécuter et est généralement exécuté plusieurs fois avec des données différentes (ou comporte des sous-parties exécutées plusieurs fois avec des données différentes). Il devient donc naturel de spécialiser un interprète pour une évaluation plus rapide d’un programme donné en l’évaluant partiellement comme argument principal. Cela peut être considéré comme un moyen de compiler le programme, et d'importants travaux de recherche ont été menés pour compiler par évaluation partielle un interprète de son premier argument (programme).
Le théorème de Smn
Le point positif de l'approche d'évaluation partielle est qu'elle prend ses racines dans la théorie (bien que la théorie puisse être un menteur), notamment dans
le théorème Smn de Kleene . J'essaie ici de donner une présentation intuitive de celle-ci, en espérant que cela ne dérangera pas les théoriciens purs.
Étant donné une numérotation Gödel de fonctions récursives, vous pouvez afficher tant que votre matériel, de sorte que, étant donné le nombre Gödel
(lire le code objet ) d’un programme, est la fonction définie par (calculée par le code objet votre matériel).φ p φ p pφφpφpp
Dans sa forme la plus simple, le théorème est énoncé dans wikipedia comme suit (avec un léger changement de notation):
Étant donné une numérotation de Gödel de fonctions récursives, il existe une fonction récursive primitive de deux arguments avec la propriété suivante: pour chaque nombre de Gödel d’une fonction partiellement calculable avec deux arguments, les expressions et sont définis pour les mêmes combinaisons de nombres naturels et , et leurs valeurs sont égales pour toute combinaison de ce type. En d’autres termes, l’égalité d’extension suivante des fonctions est valable pour tout :
φσqfφσ(q,x)(y)f(x,y)xyxφσ(q,x)≃λy.φq(x,y).
Maintenant, en prenant comme interprète , comme code source d'un programme et comme donnée pour ce programme, nous pouvons écrire:
qISxpSydφσ(IS,pS)≃λd.φIS(pS,d).
φIS peut être considérée comme l'exécution de l'interprète
sur le matériel, par exemple, comme une boîte noire prête à interpréter des programmes écrits en langage .ISS
La fonction peut être vue comme une fonction spécialisée dans l'interpréteur pour le programme , comme dans l'évaluation partielle. Ainsi, le numéro de Gödel peut être vu avec un code objet qui est la version compilée du programme .σISPSσ(IS,pS)pS
Ainsi, la fonction peut être vue comme une fonction prenant en argument le code source d'un programme
écrit en langage et la version du code objet de ce programme Donc, est ce qu'on appelle habituellement un compilateur.CS=λqS.σ((IS,qS)qSSCS
Quelques conclusions
Cependant, comme je l'ai dit: "la théorie peut être un menteur" ou semble en être un. Le problème est que nous ne savons rien de la fonction . Il existe en fait de nombreuses fonctions de ce type, et j’imagine que la démonstration du théorème peut utiliser une définition très simple, qui n’est peut-être pas meilleure, du point de vue de l’ingénierie, que la solution proposée par Raphael: regrouper simplement le théorème. le code source avec l'interpréteur . Cela peut toujours être fait pour que nous puissions dire: la compilation est toujours possible.σI SqSIS
Formaliser une notion plus restrictive de ce qu’est un compilateur nécessiterait une approche théorique plus subtile. Je ne sais pas ce qui a pu être fait dans cette direction. Le très réel travail effectué sur l’évaluation partielle est plus réaliste du point de vue de l’ingénierie. Et il y a bien sûr d'autres techniques pour écrire des compilateurs, notamment extraire des programmes de la preuve de leur spécification, développés dans le contexte de la théorie des types, basés sur l'isomorphisme de Curry-Howard (mais je sort de mon domaine de compétence) .
Mon but ici est de montrer que la remarque de Raphaël n'est pas "folle", mais un rappel sensé que les choses ne sont pas évidentes, ni même simples. Dire que quelque chose est impossible est une déclaration forte qui nécessite des définitions précises et une preuve, ne serait-ce que pour comprendre exactement comment et pourquoi c'est impossible . Mais construire une formalisation appropriée pour exprimer une telle preuve peut être assez difficile.
Cela dit, même si une fonctionnalité spécifique n'est pas compilable, au sens où l'entendent les ingénieurs, les techniques de compilation standard peuvent toujours être appliquées à des parties des programmes qui n'utilisent pas une telle fonctionnalité, comme le remarque la réponse de Gilles.
Pour faire suite aux remarques clés de Gilles selon lesquelles, en fonction de la langue, certaines actions peuvent être effectuées au moment de la compilation, tandis que d'autres doivent l'être au moment de l'exécution, ce qui nécessite un code spécifique. Nous pouvons donc voir que le concept de compilation est en réalité mal défini et n'est probablement pas définissable de manière satisfaisante. La compilation n'est qu'un processus d'optimisation, comme j'ai essayé de le montrer dans la section d' évaluation partielle , lorsque je l'ai comparé au prétraitement de données statiques dans certains algorithmes.
En tant que processus d'optimisation complexe, le concept de compilation appartient en réalité à un continuum. Selon les caractéristiques de la langue ou du programme, certaines informations peuvent être disponibles de manière statique et permettre une meilleure optimisation. D'autres choses doivent être reportées à l'exécution. Lorsque les choses vont vraiment mal, tout doit être fait au moment de l'exécution, au moins pour certaines parties du programme, et vous ne pouvez faire qu'encoder le code source avec l'interpréteur. Ce regroupement n’est donc que le bas de ce continuum de compilation. Une grande partie de la recherche sur les compilateurs consiste à trouver des moyens de faire de manière statique ce qui était fait de manière dynamique. La récupération de place au moment de la compilation semble un bon exemple.
Notez que dire que le processus de compilation devrait produire du code machine n’est pas une aide. C’est précisément ce que le groupement peut faire car l’interprète est un code machine (enfin, la compilation croisée peut compliquer un peu les choses).