43 votes

L'homoiconicité, comment ça marche?

Quelqu'un peut-il suggérer des articles expliquant le concept d'homoiconicité, en particulier en utilisant Clojure. Pourquoi est-ce que Clojure est homoiconique mais difficile à faire dans d'autres langages tels que Java?

27voto

Michał Marczyk Points 54179

Avant de poursuivre avec certaines choses que je voulais ajouter une autre réponse, en voici une de plus de référence -- la partie relative à homoiconicity est assez court, mais il est Riche Hickey faire l'expliquer! Canal 9 a cette belle vidéo avec Rich Hickey et Brian Beckman parler de Clojure. La simultanéité est, à juste titre, l'accent, mais homoiconicity est son propre (court) moment de temps d'écran au cours de laquelle des Riches explique bien l'interaction entre l' read (la fonction qui convertit syntaxe concrète comme écrit par le programmeur à l'intérieur de la représentation construite à partir des listes etc.) et eval. Il a cette belle diagramme montrant comment eval jamais même sait que le code qu'il évalue vient d' read d'exploitation sur un fichier texte... Arthur a déjà expliqué le sens derrière tout ça, mais bon, le regarder de toute façon, c'est une très belle vidéo!


Un avertissement: je vais mentionner Java et Python comme les exemples ci-dessous les prochains barre horizontale. Je tiens à préciser que ce qui suit est juste un croquis de pourquoi je pense qu'il pourrait être difficile de faire une homoiconic, Lisp-style-macro-enabled Java ou Python, c'est juste un exercice académique, bien que, et je ne veux pas examiner la question de savoir s'il n'y a aucune raison d'essayer en premier lieu. Aussi, je ne veux surtout pas dire que la syntaxe d'un langage Lisp style macros doivent contenir des délimiteurs explicites pour les structures en arbre; Dylan (la parenthèse-moins Lisp?) apparemment fournit un contre-exemple. Enfin, j'utilise l'expression Lisp style macros parce que je suis le seul à l'examen de Lisp style de macros. La langue de Suite, par exemple, a un autre système de macro dont je ne comprends pas vraiment, sauf que je sais qu'il activer wicked cool à la recherche de code. Apparemment, la syntaxe extensions peuvent être mis en œuvre dans un certain nombre de façons. Avec ce...


Je voudrais aborder la deuxième partie de votre question-comment est-ce que la plupart des langages de programmation ne sont pas considérés comme homoiconic? Je vais avoir à toucher à la sémantique de Lisp dans le processus, mais depuis que Nils a déjà fourni des liens vers des sources d'information sur le terme "homoiconic" lui-même et Arthur a décrit le lire -> macro développez -> compiler cycle, comme l'a constaté en Clojure, je vais construire sur que dans ce qui suit. Pour démarrer les choses, permettez-moi de citer un passage d'Alan Kay (extrait de l'article de Wikipédia qui a également des liens vers la source d'origine):

[...] Interactif LISP [...] et de TRAC [...] les deux sont des "homoiconic" dans leurs internes et externes, les représentations sont essentiellement les mêmes.

(Ceux [...] les bits de cacher beaucoup de texte, mais l'essentiel est inchangé.)

Maintenant, nous allons nous poser la question: qu'est-ce que Java représentation interne de Java? ... Eh bien, ce n'est même pas de sens. La Java compilateur a une certaine représentation interne de Java, à savoir un arbre de syntaxe abstraite, de construire un "homoiconic Java", nous aurions à faire que AST représentation de la première classe de l'objet en Java et élaborer une syntaxe, ce qui nous permettrait d'écrire ASTs directement. Qui pourraient s'avérer être plutôt dur.

Python fournit un exemple de non-homoiconic langue qui est intéressant en ce qu'il est actuellement livré avec un AST-manipulation de la boîte à outils de la forme de l' ast module. La documentation de ce module d'indiquer explicitement que le Python ASTs peut changer entre les versions, ce qui peut ou peut ne pas être décourageant; encore, je suppose, un travailleur acharné, programmeur pourrait prendre l' ast module d'élaborer une syntaxe (peut-être S-basé sur une expression, peut-être basé sur XML) pour la description de Python ASTs directement et de construire un analyseur syntaxique pour que la syntaxe régulièrement en Python à l'aide de ast, prenant ainsi une bonne première étape vers la création d'un homoiconic langue avec Python sémantique. (Je crois que je suis tombé sur un dialecte de Lisp de la compilation de bytecode Python il y a quelques temps... je me demande si il peut faire quelque chose comme ça à un certain niveau?)

Même alors, le problème reste de l'extraction des avantages concrets de ce genre de homoiconicity. Il est considéré comme une propriété bénéficiaire des membres de la famille du Lisp de langues, car il permet d'écrire des programmes d'écrire de nouveaux programmes, parmi lesquels les macros sont les plus notables. Maintenant, alors que les macros sont activées dans un sens par le fait qu'il est si facile de manipuler la représentation interne de code Lisp en Lisp, ils sont également permis dans une égale façon importante par le Lisp modèle d'exécution: un programme Lisp est juste une collection de Lisp formes; celles-ci sont traitées par la fonction Lisp eval qui est chargée de déterminer les valeurs des expressions et de provoquer le côté approprié-effets au bon moment; la sémantique de Lisp sont exactement la sémantique d' eval. La question de savoir comment les choses fonctionnent en interne pour préserver cette sémantique de l'illusion, tout en étant assez rapide est un détail d'implémentation; un système Lisp a l'obligation d'exposer une fonction eval pour le programmeur et d'agir comme si Lisp programmes étaient en cours de traitement par cette fonction.

Moderne Lisp systèmes, c'est une partie de l' evals'contrat qu'il effectue une autre prétraitement de la phase au cours de laquelle les macros sont développées avant l'évaluation de la code (ou de la compilation et de l'exécution, selon le cas peut être). Cette facilité n'est pas une partie nécessaire d'un système Lisp, mais il est tellement facile de le brancher sur ce modèle d'exécution! Aussi, je me demande si ce n'est pas le seul modèle d'exécution qui rend le Lisp sorte de macro transformations gérable, ce qui signifie que n'importe quelle langue cherchant à intégrer Lisp-style macros aurait à adopter un semblable modèle d'exécution. Mon intuition me dit que c'est effectivement le cas.

Bien sûr, une fois qu'une langue est écrite en notation directement parallèlement à ses ASTs et utilise un Lisp comme modèle d'exécution avec un évaluateur de fonction / objet, on doit se demander si elle n'est pas, par hasard, un autre dialecte de Lisp... même si ses AST-mise en parallèle de la syntaxe se trouve être basé sur XML. frisson

9voto

Arthur Ulfeldt Points 45059

Quand j'étais en train d'apprendre Lisp l'idée de homoiconicity avait du sens quand j'ai appris que le lisp est "compilé" en deux phases, la lecture et la compilation le code est représenté avec la même structure de données pour ces deux:

  • d'abord, vous pensez à une s-expression dans votre tête
  • ensuite, vous tapez la s-expression des caractères dans un fichier
  • alors le lecteur convertit les caractères dans le fichier s-expressions. Ses pas compiler le programme, juste la construction de structures de données de caractères c'est la partie de la lecture de la phase.
  • ensuite, le lecteur regarde chacune des expressions et décidé s'ils sont d'une macro et si oui, exécute la macro pour produire une s-expression. donc, à ce stade, nous avons passé de s-expressions des personnages de la s-expressions, puis à partir de s-expressions différentes s-expressions.
  • ces s-expressions sont ensuite compilées dans .les fichiers de classe qui peut être exécuté par la jvm, c'est le deuxième de la "compilation" de phase.

Donc, son joli beaucoup s-expressions tout le chemin de votre cerveau .fichier de classe. vous même écrire des s-expressions qui écrivent des s-expressions. donc on peut dire que "le code est dans les données" ou "code de données" parce que cela sonne mieux.

8voto

Rainer Joswig Points 62532

L'idée de "homoiconicity' est un peu confus et ne rentre pas bien dans le langage Lisp. Internes et externes, les représentations ne sont pas les mêmes en Lisp. La représentation externe est basée sur des caractères dans les fichiers. La représentation interne est basé sur le langage Lisp données (nombres, chaînes de caractères, listes, tableaux, ...) et est non-textuel. Comment est-ce la même chose que de caractères?

La principale différence entre Lisp et de nombreux autres langages de programmation est, que Lisp est une simple représentation de données et de code source qui n'est pas basé sur des chaînes de caractères. Évidemment le code peut être représenté sous forme de chaînes dans les langages de programmation textuels. Mais en Lisp de la source peut être représentée en termes de primitives Lisp structures de données. La représentation externe est basée sur la s-expressions, qui est un modèle simple pour représenter hiérarchique des données sous forme de texte. Le modèle interne est la représentation est basée sur des listes, etc.

Le modèle de base:

  • LIRE traduit externe s-expressions de données
  • EVAL prend Lisp formes dans la forme de Lisp données et les évalue
  • IMPRIMER traduit Lisp données en externe s-expressions

Notez que de LIRE et d'IMPRIMER le travail pour arbitraire Lisp de données, et pas seulement pour Lisp formes.

3voto

Voici un programme court pour faire la différenciation symbolique. Ceci est un exemple de LISP manipulant son propre code. Essayez de le traduire dans une autre langue pour comprendre pourquoi LISP convient à ce genre de chose.

 ;; The simplest possible symbolic differentiator

;; Functions to create and unpack additions like (+ 1 2)
(defn make-add [ a b ] (list '+ a b))
(defn addition? [x] (and (=(count x) 3) (= (first x) '+)))
(defn add1   [x] (second x))
(defn add2   [x] (second (rest x)))

;; Similar for multiplications (* 1 2)
(defn make-mul [ a b ] (list '* a b))
(defn multiplication? [x] (and (=(count x) 3) (= (first x) '*)))
(defn mul1   [x] (second x))
(defn mul2   [x] (second (rest x)))

;; Differentiation. 
(defn deriv [exp var]
  (cond (number? exp) 0                                                              ;; d/dx c -> 0
        (symbol? exp) (if (= exp var) 1 0)                                           ;; d/dx x -> 1, d/dx y -> 0
        (addition? exp) (make-add (deriv (add1 exp) var) (deriv (add2 exp) var))     ;; d/dx a+b -> d/dx a + d/dx b
        (multiplication? exp) (make-add (make-mul (deriv (mul1 exp) var) (mul2 exp)) ;; d/dx a*b -> d/dx a * b + a * d/dx b
                                        (make-mul (mul1 exp) (deriv (mul2 exp) var)))
        :else :error))

;;an example of use: create the function x -> x^3 + 2x^2 + 1 and its derivative 
(def poly '(+ (+ (* x (* x x)) (* 2 (* x x))) 1))

(defn poly->fnform [poly] (list 'fn '[x] poly))

(def polyfn  (eval (poly->fnform poly)))
(def dpolyfn (eval (poly->fnform (deriv poly 'x))))
 

0voto

Nils Schmidt Points 2128

Cela semble presque évident, mais les premières sources pourraient être:

http://en.wikipedia.org/wiki/Homoiconicity

http://c2.com/cgi/wiki?DefinitionOfHomoiconic

L'homoiconicité est expliquée en général et vous pouvez également trouver les sources d'origine. Comme cela est expliqué en utilisant l'exemple de Lisp, ce n'est pas si loin de Clojure.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X