41 votes

Un langage peut-il avoir les puissantes macros de Lisp sans les parenthèses ?

Un langage peut-il avoir les puissantes macros de Lisp sans les parenthèses ?

1 votes

Je pense qu'une meilleure façon de formuler cette question serait : Est-ce qu'un langage peut avoir les puissantes macros de Lisp sans que le code soit écrit comme un ensemble de structures de données et qu'il offre la même interface uniforme pour manipuler ces structures de données que celle offerte par le code "normal" ?

0 votes

1 votes

@DNolen Cela implique de montrer que la même chose peut être accomplie par l'utilisation, par exemple, d'espaces blancs ou d'autres moyens de structurer le code.

48voto

Rainer Joswig Points 62532

Bien sûr, la question est de savoir si la macro est pratique à utiliser et quelle est sa puissance.

Voyons d'abord comment Lisp est légèrement différent.

La syntaxe Lisp est basée sur les données, pas sur le texte.

Lisp possède une syntaxe à deux niveaux.

A) il y a d'abord la syntaxe des données pour les expressions s

exemples :

(mary called tim to tell him the price of the book)

(sin ( x ) + cos ( x ))

Les expressions s sont des atomes, des listes d'atomes ou des listes.

B) ensuite, il y a la syntaxe du langage Lisp qui s'ajoute aux s-expressions. Toutes les s-expressions ne sont pas des programmes Lisp valides.

(3 + 4)

n'est pas un programme Lisp valide, car Lisp utilise la notation par préfixe.

(+ 3 4)

est un programme Lisp valide. Le premier élément est une fonction - ici la fonction + .

Les expressions S sont des données

La partie intéressante est maintenant que les expressions s peuvent être lues et que Lisp utilise les structures de données normales (nombres, symboles, listes, chaînes) pour les représenter.

La plupart des autres langages de programmation ne disposent pas d'une représentation primitive pour la source internalisée - autre que les chaînes de caractères.

Notez que les expressions s ne représentent pas ici un AST (Abstract Syntax Tree). Il s'agit plutôt d'un arbre token hiérarchique issu d'une phase de lexer. Un lexeur identifie les éléments lexicaux.

Le code source internalisé permet maintenant de calculer facilement avec du code, car les fonctions habituelles pour manipuler les listes peuvent être appliquées.

Manipulation simple du code avec les fonctions de liste

Regardons le code Lisp invalide :

(3 + 4)

Le programme

(defun convert (code)
   (list (second code) (first code) (third code)))

(convert '(3 + 4)) -> (+ 3 4)

a converti une expression infixe en une expression préfixe Lisp valide. Nous pouvons donc l'évaluer.

(eval (convert '(3 + 4))) -> 7

EVAL évalue le code source converti. eval prend en entrée une expression s, ici une liste (+ 3 4) .

Comment calculer avec le code ?

Les langages de programmation disposent désormais d'au moins trois choix pour rendre possible le calcul des sources :

  1. baser les transformations du code source sur les transformations des chaînes de caractères

  2. utilisent une structure de données primitive similaire à celle de Lisp. Une variante plus complexe de ceci est une syntaxe basée sur XML. On pourrait alors transformer des expressions XML. Il existe d'autres formats externes possibles combinés à des données internalisées.

  3. utiliser un véritable format de description syntaxique et représenter le code source internalisé comme un arbre syntaxique utilisant des structures de données qui représentent les catégories syntaxiques. -> utiliser un AST.

Pour toutes ces approches, vous trouverez des langages de programmation. Lisp se trouve plus ou moins dans le camp 2. La conséquence : il n'est théoriquement pas vraiment satisfaisant et rend impossible l'analyse statique du code source (si les transformations du code sont basées sur des fonctions Lisp arbitraires). La communauté Lisp se bat avec cela depuis des décennies (voir par exemple la myriade d'approches que la communauté Scheme a essayé). Heureusement, il est relativement facile à utiliser, comparé à certaines des alternatives et assez puissant. La variante 1 est moins élégante. La variante 3 conduit à beaucoup de complexité dans les transformations simples ET complexes. Cela signifie généralement aussi que l'expression a déjà été analysée par rapport à une grammaire de langage spécifique.

Un autre problème est de savoir COMMENT transformer le code. Une approche serait basée sur des règles de transformation (comme dans certaines variantes de macro Scheme). Une autre approche serait un langage de transformation spécial (comme un langage de template qui peut faire des calculs arbitraires). L'approche Lisp consiste à utiliser Lisp lui-même. Cela permet d'écrire des transformations arbitraires en utilisant le langage Lisp complet. En Lisp, il n'y a pas d'étape d'analyse séparée, mais à tout moment, les expressions peuvent être lues, transformées et évaluées - car ces fonctions sont à la disposition de l'utilisateur.

Lisp est en quelque sorte un maximum local de simplicité pour les transformations de code.

Autre syntaxe frontale

Notez également que la fonction read lit les expressions s en données internes. En Lisp, on peut soit utiliser un autre lecteur pour une syntaxe externe différente ou réutiliser le lecteur intégré de Lisp et le reprogrammer en utilisant le mécanisme de macro lecture - ce mécanisme permet d'étendre ou de modifier la syntaxe des expressions s. Il existe des exemples pour les deux approches permettant de fournir une syntaxe externe différente en Lisp.

Il existe par exemple des variantes de Lisp qui ont une syntaxe plus conventionnelle, où le code est analysé en expressions s.

Pourquoi la syntaxe basée sur les expressions s est-elle populaire parmi les programmeurs Lisp ?

La syntaxe Lisp actuelle est populaire parmi les programmeurs Lisp pour deux raisons :

1) le données est code est données Cette idée permet d'écrire facilement toutes sortes de transformations de code basées sur les données internalisées. Il existe également un chemin relativement direct entre la lecture du code, la manipulation du code et l'impression du code. Les outils de développement habituels peuvent être utilisés.

2) l'éditeur de texte peut être programmé de manière directe pour manipuler les expressions s. Cela rend les transformations de base du code et des données dans l'éditeur relativement faciles.

À l'origine, on pensait que Lisp avait une syntaxe différente, plus conventionnelle. Plusieurs tentatives ont été faites par la suite pour passer à d'autres variantes de syntaxe, mais pour certaines raisons, elles ont échoué ou ont donné naissance à d'autres langages.

0 votes

Salut Rainer. Pouvez-vous nous en dire plus sur The consequence: it is theoretically not really satisfying and makes it impossible to statically parse source code (if the code transformations are based on arbitrary Lisp functions). Si cela n'est théoriquement pas vraiment satisfaisant, qu'est-ce qui serait satisfaisant alors ?

0 votes

@Faheem Mitha : utiliser un vrai langage de description syntaxique serait théoriquement mieux que d'utiliser des structures de données Lisp (listes, symboles, ...).

1 votes

Intéressant. J'aimerais en savoir plus sur ce que vous avez à l'esprit ici. Pouvez-vous vous étendre sur ce sujet ? Ou avez-vous un lien pertinent ?

22voto

Ken Points 276

Absolument. C'est juste quelques ordres de grandeur plus complexes, si vous devez vous occuper d'une grammaire complexe. Comme Peter Norvig a noté :

Python a accès à l'arbre syntaxique l'arbre syntaxique abstrait des programmes, mais ce n'est pas pour les âmes sensibles. Sur du côté positif, les modules sont faciles à faciles à comprendre, et avec cinq minutes et cinq lignes de code, j'ai pu obtenir ceci :

>>> parse("2 + 2")

['eval_input', ['testlist', ['test', ['and_test', ['not_test', ['comparison', ['expr', ['xor_expr', ['and_expr', ['shift_expr', ['arith_expr', ['term', ['factor', ['power', ['atom', [2, '2']]]]], [14, '+'], ['term', ['factor', ['power', ['atom', [2, '2']]]]]]]]]]]]]]], [4, ''], [0, '']]

C'était plutôt une déception pour moi. L'analyse Lisp de l'expression équivalente est la suivante (+ 2 2) . Il semble que seul un véritable expert souhaiterait manipuler les arbres d'analyse de Python, alors que les arbres d'analyse de Lisp sont simples à utiliser pour tout le monde. Il est toujours possible de créer quelque chose de similaire aux macros en Python en concaténant des chaînes de caractères, mais cela n'est pas intégré au reste du langage, et n'est donc pas fait en pratique.

Comme je ne suis pas un super-génie (ni même un Peter Norvig), je m'en tiendrai à (+ 2 2) .

6 votes

+1, pour m'avoir fourni un lien vers une lecture très intéressante :)

0 votes

Néanmoins, il est très facile d'analyser des expressions Lisp en utilisant Python Il est donc possible de traduire des expressions Lisp en code source Python.

15voto

Xanthir Points 7035

Voici une version plus courte de la réponse de Rainer :

Afin d'avoir des macros de style lisp, vous avez besoin d'un moyen de représenter le code source dans des structures de données. Dans la plupart des langages, la seule "structure de données du code source" est une chaîne de caractères, qui n'a pas de fonction presque assez de structure pour vous permettre de faire de vraies macros dessus. Certains langages offrent une vraie structure de données, mais elle est trop complexe, comme Python, de sorte qu'écrire de vraies macros est stupidement compliqué et ne vaut pas vraiment la peine.

Les listes et les parenthèses de Lisp ont trouvé le juste milieu. Juste assez de structure pour la rendre facile à manipuler, mais pas trop pour ne pas se noyer dans la complexité. En prime, lorsque vous imbriquez des listes, vous obtenez un arbre, qui se trouve être précisément la structure que les langages de programmation adoptent naturellement (presque tous les langages de programmation sont d'abord analysés dans un "arbre syntaxique abstrait", ou AST, avant d'être réellement interprétés/compilés).

En fait, programmer Lisp, c'est écrire directement un AST, plutôt que d'écrire un autre langage qui est ensuite transformé en AST par l'ordinateur. Vous pourriez éventuellement renoncer aux parenthèses, mais vous auriez besoin d'un autre moyen pour regrouper les éléments dans une liste/arbre. Vous ne gagneriez probablement pas beaucoup à le faire.

2 votes

chrisdone.com/z est une expérience qui montre que les macros basées sur des chaînes de caractères ne sont pas horribles, si l'on dispose d'une structure minimale permettant de savoir où se termine le paramètre. TCL est similaire - avec un peu de support pour les fonctions {...} Avec une délimitation imbriquée et un certain échappement, il est facile d'écrire des commandes d'ordre supérieur qui sont en fait des macros basées sur des chaînes de caractères. TCL est également assez homoiconique - il utilise la même structure d'accolades imbriquées pour les données. Pourtant, ce n'est pas vraiment joli (ou rapide) comparé aux vrais arbres de Lisp - il a été dit que "TCL est Lisp sous drogues".

9voto

Les parenthèses ne sont pas pertinentes pour les macros. C'est juste la façon de faire de Lisp.

Par exemple, Prolog possède un mécanisme de macros très puissant appelé "expansion de termes". Fondamentalement, chaque fois que Prolog lit un terme T, il essaie une règle spéciale term_expansion(T, R) . Si elle réussit, le contenu de R est interprété à la place de T.

0 votes

Je suis curieux de savoir si ce mécanisme a accès à l'ensemble du langage Prolog pour effectuer l'expansion ? Wikipedia donne l'impression que c'est quelque chose qui se passe à une étape du préprocesseur, ce qui le rend beaucoup moins utile que les macros Lisp si c'est vrai.

1 votes

L'expansion des termes, au moins dans SWI-Prolog, a accès à l'ensemble du langage Prolog. y à chaque trimestre précédent. Il ne s'agit pas d'un préprocesseur, mais d'une partie de l'interpréteur/compilateur lui-même.

5voto

Dirk Points 17809

Sans parler de la Langue Dylan Il s'agit d'un langage infixe (style Algol), qui possède un système de macros syntaxiques assez puissant, qui offre (entre autres) la transparence référentielle, tout en étant un langage infixe (style Algol).

2 votes

Cela semble beaucoup plus compliqué que les macros de style Common Lisp. Je suppose que c'est ce qui caractérise les macros de style CL : elles sont à la fois simples (car on peut comprendre comment les écrire) et puissantes. Peut-être qu'un jour plus de programmeurs comprendront les macros de style CL et que nous passerons au niveau supérieur.

1 votes

@dnolen : Common Lisp n'est pas le seul Lisp. Les macros de Dylan sont basées sur les macros hygiéniques de Scheme, qui ont une syntaxe un peu plus compliquée mais présentent d'autres avantages.

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