• [^] # Re: C'est bien dommage

    Posté par . En réponse au journal C++17 est sur les rails. Évalué à 2.

    Effectivement je ne connaissais pas la terminologie Lisp, une macro n'est rien d'autre qu'une fonction si je comprend bien ? ou sont-ce des fonctions d'une forme particulière ?

    En pratique elles sont définies avec defmacro plutôt que defun, mais a priori derrière ça génère une fonction. À l'intérieur c'est juste du code Lisp, comme dans une fonction.

    Première différence importante : alors que pour une fonction les paramètres sont évalués puis leur valeur passée à la fonction, pour une macro les expressions sont passées telles quelles. Donc si j'écris le code :

    (truc 1 (+ 1 2))
    

    Si truc est une fonction, elle reçoit en paramètres 1 et 3, qui est le résultat de l'évaluation de l'expression (+ 1 2).
    Si truc est une macro, elle reçoit en paramètre 1 et (+ 1 2), qui est une liste contenant 3 valeurs : un symbole et deux nombres. En Lisp les symboles sont des objets manipulables, comme les fonctions, les nombres, les chaînes et autres : ils peuvent être stockés dans une variable, dans une liste, ... comme n'importe quelle autre valeur.

    Deuxième différence importante : les macros renvoient une expression Lisp (une valeur simple ou une liste, cette liste représentant le code qui remplacera l'utilisation de la macro). La macro reçoit donc des paramètres et exécute du code Lisp qui génère le code qui doit la remplacer.

    Troisième différence importante : les macros sont évidemment exécutées au moment de la compilation : dans mon exemple précédent, si truc est une macro le compilateur exécute la macro en lui passant les deux expressions, récupère une nouvelle expression qui remplace celle qui se trouvait dans le code. Puis il continue son parcours (si la racine de l'expression résultat est à nouveau une macro il la développe).

    De fait la syntaxe Lisp représente directement son AST, là où en OCaml on passe par des modules du compilateur pour le manipuler. Mais il me semble que c'est essentiellement pour des raisons de typage : le Lisp est du lambda-calcul faiblement typé, là où OCaml est du lambda-calcul fortement typé statiquement.

    En fait Lisp est plutôt fortement typé, même par rapport à Java (que je connais mieux qu'OCaml). Par rapport à OCaml je ne sais pas, mais "faiblement typé" je ne pense pas. Par contre c'est typé dynamiquement, effectivement. Tout ça si on suit le découpage suivant, qui représente bien les choses je trouve : typage faible/fort, statique/dynamique, explicite/implicite. Pour Lisp c'est typage fort, dynamique et implicite, pour OCaml typage fort, statique et implicite (si j'ai bien compris, c'est implicite et on précise dans les cas où le compilateur est perdu, la plupart du temps il se débrouille pour déduire les types).

    La "syntaxe" de Lisp est une représentation assez simple de l'AST. L'intérêt de la notation préfixée c'est que les parenthèses délimitent des listes et qu'une macro n'est qu'une fonction un peu particulière qui reçoit des listes en paramètre et qui doit générer une liste (l'arbre représentant l'expression qui la remplace). Et tout ça est assez simple à manipuler avec les fonctions de base du langage.

    Pour OCaml je ne connais pas donc j'ai un peu de mal à comparer avec les outils de manipulation de l'arbre. En Lisp il faut générer l'arbre pour vérifier le code par exemple (visuellement, en l'exécutant ou avec un analyseur de code, mais pour avoir une idée du code généré il faut exécuter la macro), avec OCaml je ne sais pas si le compilateur doit générer l'arbre pour le vérifier ou s'il peut vérifier directement le code de la fonction de remplacement pour savoir si le résultat sera valide, ce qui serait une différence.

    Par curiosité, dans ton exemple en OCaml tu écris ça :

    (* je code le test if-then-else en fonctionnel pure *)
    let iff b e1 e2 = if b then e1 else e2

    Dans un langage comme Java ça ne marche pas parce que les expressions e1 et e2 seraient évaluées systématiquement avant d'être passées en paramètre à la fonction iff.
    En Common Lisp ça marche si iff est une macro, ce qui permet de ne pas évaluer systématiquement les deux expressions avant de choisir le résultat à renvoyer.
    En OCaml ça marche parce que le langage est purement fonctionnel et que le compilateur va se débrouiller pour n'évaluer que les expressions nécessaires ? Ou tu as fait un exemple simplifié ?

    Sur ce, bon week-end à tout le monde :) . Je lirai la suite (s'il y a) dimanche ou lundi sans doute.