D'accord, donc en fait je pense que tu ne connais pas du tout Lisp... Comme je ne connais rien en OCaml c'est un peu délicat comme conversation :) .
Je vais essayer de préciser quelques trucs, je pense que ça va simplifier.
En Lisp tout ce qui est entre parenthèses est une liste. Donc le code source est lui même constitué de listes imbriquées : (print (+ 1 2)) est constitué d'une liste contenant le symbole "print" et une sous-liste, qui elle-même contient un symbole et deux entiers. On remarque que la notation préfixée + les imbrications de liste => on écrit directement l'AST. Contrairement à la syntaxe de la plupart des langages où on a des notations mélangées (préfixée dans le cas des fonctions, infixée dans le cas des opérateurs mathématiques par exemple), qui est traduite par l'analyseur syntaxique en un arbre, en Lisp le code représente déjà cet arbre.
Il ne faut pas confondre les macros du C avec celles de Lisp. Les macros du C font du remplacement de texte. Les macros de Lisp sont des fonctions qui reçoivent en paramètre des expressions Lisp. Donc des atomes (comme des nombres ou des chaînes) et des listes. La représentation uniforme entre le code et les données (les listes essentiellement) permet de manipuler ça très simplement. Si je crée une macro tructruc qui s'utilise de cette manière : (tructruc 1 (bidule 2)), la macro est une bout de code Lisp (comme une fonction) qui reçoit deux paramètres : le nombre entier 1 et une liste contenant un symbole ("bidule") et le nombre entier 2. L'analyse syntaxique est déjà faite, la macro reçoit en paramètre des expressions faciles à manipuler avec les opérations standards sur les listes. Et sans outils plus ou moins complexes de compilation. Pour générer du code c'est très simple : le code n'est jamais qu'une liste contenant des éléments simples et des sous-listes, le tout représentant l'AST. De ce que je comprends de ton message, OCaml est passé d'outils de bas niveau des compilateurs (analyse syntaxique notamment, avec le préprocesseur) à des outils de plus haut niveau sur les AST, ce qui est plus simple. Et ça a l'air bien, c'est plus proche du Lisp ;) . Par contre j'ai cru comprendre que ça ne permettait de créer que des variantes des opérateurs let, fun et if. Sinon la syntaxe pour définir le nouvel AST a l'air horrible (en Lisp il suffit de construire l'arbre sous forme de listes imbriquées : dans une page présentant la fonctionnalité je vois cette phrase : "For example, Exp.tuple [Exp.constant (Const_int 1); Exp.constant (Const_int 2)] would construct the AST for (1, 2). While unwieldy, this is much better than elaborating the AST directly." ; c'est vrai, mais en lisp on aurait juste écrit '(1 2)) et ça a l'air compliqué de lire les arbres/sous-arbres de l'expression d'origine pour en tirer des informations à placer dans l'arbre transformé.
Pour ce qui est de "défigurer" la syntaxe : en Lisp c'est facile, il n'y a "pas de syntaxe", juste les AST. C'est-à-dire qu'il n'y a rien à défigurer. En fait on peut modifier le sens de caractères (par exemple dire que si j'écris un truc entre crochets ça veut dire quelque chose de particulier) mais ce n'est pas avec les macros. Donc par exemple imaginons que je veux ouvrir un fichier et être sûr de le fermer correctement, même si une erreur est signalée, ... Certains langages ont une syntaxe pour ça (c'est le cas de Lisp aussi mais on va faire comme si ce n'était pas le cas). Je crée une macro with-open-file (celle qui existe déjà en Lisp, mais on aurait pu l'écrire nous-mêmes) et j'utilise ça comme ça :
(with-open-file (fic "/chemin/du/fichier")
...)
La syntaxe n'est pas défigurée, la macro a la même forme que tout le reste : une expression sous forme d'une liste dont le premier élément est le nom de la macro. Comme pour une fonction en fait (voir le code avec print plus haut). Comme un bloc try-with-resources en java.
Pareil si je veux une macro qui définit des classes ayant certaines caractéristiques : je crée une macro "def-classe" qui prend en paramètre les noms des attributs de la classe et génère tout ce que je veux (accesseurs, opérations particulières, copie des objets, ... ce qui me passe par la tête). Et ça s'utilisera comme ça :
(def-classe personne
nom
prenom
date-naissance)
Il n'y a rien de défiguré : ça ressemble à l'utilisation de la macro defclass de Lisp (qui permet de définir les classes).
Défigurer la syntaxe c'est beaucoup plus facile dans les langages qui ont une syntaxe.
Pour ce qui est du lambda-calcul : euh... oui, bon, le lambda-calcul c'est un truc un peu théorique quand même :) . Si la syntaxe de Lisp colle à quelque chose c'est aux AST. Pour échanger les valeurs de deux variables on peut écrire (rotatef x y). Pour plus je ne sais pas s'il existe une macro (rotatef les décale d'un rang, donc pour 3 ça doit aller mais au-delà c'est moins évident ; d'un autre côté utiliser une seule instruction pour permuter plein de valeurs, bon... ou alors il y a let). En cas de besoin on pourrait écrire une macro qui s'utilise comme ça par exemple :
(swap (x y z) (z x y))
Ou bien comme ça :
(swap (x z) (y x) (z y))
pour mieux voir les paires.
On peut faire avec moins de parenthèses, mais séparer les deux listes c'est plus clair qu'écrire (swap x y z z x y).
Rien à voir avec l'expression de lambda-calcul que tu indiques, quelle idée... :)
[^] # Re: C'est bien dommage
Posté par ylsul . En réponse au journal C++17 est sur les rails. Évalué à 3.
D'accord, donc en fait je pense que tu ne connais pas du tout Lisp... Comme je ne connais rien en OCaml c'est un peu délicat comme conversation :) .
Je vais essayer de préciser quelques trucs, je pense que ça va simplifier.
En Lisp tout ce qui est entre parenthèses est une liste. Donc le code source est lui même constitué de listes imbriquées :
(print (+ 1 2))est constitué d'une liste contenant le symbole "print" et une sous-liste, qui elle-même contient un symbole et deux entiers. On remarque que la notation préfixée + les imbrications de liste => on écrit directement l'AST. Contrairement à la syntaxe de la plupart des langages où on a des notations mélangées (préfixée dans le cas des fonctions, infixée dans le cas des opérateurs mathématiques par exemple), qui est traduite par l'analyseur syntaxique en un arbre, en Lisp le code représente déjà cet arbre.Il ne faut pas confondre les macros du C avec celles de Lisp. Les macros du C font du remplacement de texte. Les macros de Lisp sont des fonctions qui reçoivent en paramètre des expressions Lisp. Donc des atomes (comme des nombres ou des chaînes) et des listes. La représentation uniforme entre le code et les données (les listes essentiellement) permet de manipuler ça très simplement. Si je crée une macro tructruc qui s'utilise de cette manière :
(tructruc 1 (bidule 2)), la macro est une bout de code Lisp (comme une fonction) qui reçoit deux paramètres : le nombre entier 1 et une liste contenant un symbole ("bidule") et le nombre entier 2. L'analyse syntaxique est déjà faite, la macro reçoit en paramètre des expressions faciles à manipuler avec les opérations standards sur les listes. Et sans outils plus ou moins complexes de compilation. Pour générer du code c'est très simple : le code n'est jamais qu'une liste contenant des éléments simples et des sous-listes, le tout représentant l'AST. De ce que je comprends de ton message, OCaml est passé d'outils de bas niveau des compilateurs (analyse syntaxique notamment, avec le préprocesseur) à des outils de plus haut niveau sur les AST, ce qui est plus simple. Et ça a l'air bien, c'est plus proche du Lisp ;) . Par contre j'ai cru comprendre que ça ne permettait de créer que des variantes des opérateurs let, fun et if. Sinon la syntaxe pour définir le nouvel AST a l'air horrible (en Lisp il suffit de construire l'arbre sous forme de listes imbriquées : dans une page présentant la fonctionnalité je vois cette phrase : "For example,Exp.tuple [Exp.constant (Const_int 1); Exp.constant (Const_int 2)]would construct the AST for (1, 2). While unwieldy, this is much better than elaborating the AST directly." ; c'est vrai, mais en lisp on aurait juste écrit'(1 2)) et ça a l'air compliqué de lire les arbres/sous-arbres de l'expression d'origine pour en tirer des informations à placer dans l'arbre transformé.Pour ce qui est de "défigurer" la syntaxe : en Lisp c'est facile, il n'y a "pas de syntaxe", juste les AST. C'est-à-dire qu'il n'y a rien à défigurer. En fait on peut modifier le sens de caractères (par exemple dire que si j'écris un truc entre crochets ça veut dire quelque chose de particulier) mais ce n'est pas avec les macros. Donc par exemple imaginons que je veux ouvrir un fichier et être sûr de le fermer correctement, même si une erreur est signalée, ... Certains langages ont une syntaxe pour ça (c'est le cas de Lisp aussi mais on va faire comme si ce n'était pas le cas). Je crée une macro with-open-file (celle qui existe déjà en Lisp, mais on aurait pu l'écrire nous-mêmes) et j'utilise ça comme ça :
La syntaxe n'est pas défigurée, la macro a la même forme que tout le reste : une expression sous forme d'une liste dont le premier élément est le nom de la macro. Comme pour une fonction en fait (voir le code avec print plus haut). Comme un bloc try-with-resources en java.
Pareil si je veux une macro qui définit des classes ayant certaines caractéristiques : je crée une macro "def-classe" qui prend en paramètre les noms des attributs de la classe et génère tout ce que je veux (accesseurs, opérations particulières, copie des objets, ... ce qui me passe par la tête). Et ça s'utilisera comme ça :
Il n'y a rien de défiguré : ça ressemble à l'utilisation de la macro defclass de Lisp (qui permet de définir les classes).
Défigurer la syntaxe c'est beaucoup plus facile dans les langages qui ont une syntaxe.
Pour ce qui est du lambda-calcul : euh... oui, bon, le lambda-calcul c'est un truc un peu théorique quand même :) . Si la syntaxe de Lisp colle à quelque chose c'est aux AST. Pour échanger les valeurs de deux variables on peut écrire
(rotatef x y). Pour plus je ne sais pas s'il existe une macro (rotatef les décale d'un rang, donc pour 3 ça doit aller mais au-delà c'est moins évident ; d'un autre côté utiliser une seule instruction pour permuter plein de valeurs, bon... ou alors il y a let). En cas de besoin on pourrait écrire une macro qui s'utilise comme ça par exemple :Ou bien comme ça :
pour mieux voir les paires.
On peut faire avec moins de parenthèses, mais séparer les deux listes c'est plus clair qu'écrire
(swap x y z z x y).Rien à voir avec l'expression de lambda-calcul que tu indiques, quelle idée... :)