Macros

Clojure dispose d'un système de macro permettant de créer des abstractions complexes pouvant servir notamment à ajouter des fonctionnalités au langage. En réalité, beaucoup d'éléments présentés dans ce tutoriel sont des macros et non pas des fonctions (cond, condp, for, when...) et certaines librairies comme core.async est majoritement écrite avec des macros.

Une macro se définit avec defmacro. La différence entre une fonction et une macro est que dans cette dernière les arguments ne sont pas évalués avant d'être transformés par la macro. La macro traite les données reçues en argument afin de produire des nouvelles forms.

Imaginons que nous souhaitions réaliser une notation infix sous forme de fonction : (infix (2 + 3)). Ce ne serait pas possible car Clojure va d'abord évaluer (2 + 3) qui retournera une exception.

Maintenant, sous forme de macro :

(defmacro infix [coll]
  (list (second coll) (first coll (last coll))))

(infix (2 + 3))

La macro reçoit en paramètre une liste contenant (2 + 3) puis réécrit une form valide(+ 2 3).

Grâce à macroexpand on comprend aisément comment une macro est évaluée.

(macroexpand '(infix (2 + 3)))
(+ 2 3)

La philosophie des Lisp "data as code" prend avec les macros tout son sens. On prend une structure de données, on la transforme en une autre structure de données qui correspond maintenant à du code à exécuter.

Précédent - -

Powered by mcorbin - Available on Github