Common Lisp vous permet d'écrire des macros qui effectuent la transformation source que vous souhaitez.
Scheme vous offre un système hygiénique de correspondance de motifs qui vous permet également d'effectuer des transformations. Quelle est l'utilité des macros dans la pratique? Paul Graham a déclaré dans Beating the Averages que:
Le code source de l'éditeur Viaweb était probablement composé d'environ 20-25% de macros.
Quelles sortes de choses les gens finissent-ils par faire avec les macros?
Réponses:
Jetez un œil à cet article de Matthias Felleisen sur la liste de discussion LL1 en 2002. Il suggère trois utilisations principales des macros:
la source
J'utilise principalement des macros pour ajouter de nouvelles constructions de langage permettant de gagner du temps, ce qui nécessiterait autrement un tas de code standard.
Par exemple, je me suis récemment retrouvé à vouloir un impératif
for-loop
similaire à C ++ / Java. Cependant, étant un langage fonctionnel, Clojure n'est pas venu avec un hors de la boîte. Je l'ai donc implémenté comme une macro:Et maintenant je peux faire:
Et voilà: une nouvelle construction de langage de compilation à usage général en six lignes de code.
la source
Écriture d'extensions de langage ou DSL.
Pour avoir une idée de cela dans les langages de type Lisp, étudiez Racket , qui a plusieurs variantes de langage: Typed Racket, R6RS et Datalog.
Voir aussi le langage Boo, qui vous donne accès au pipeline du compilateur dans le but spécifique de créer des langages spécifiques au domaine via des macros.
la source
Voici quelques exemples:
Schème:
define
pour les définitions de fonction. Fondamentalement, cela permet de définir une fonction plus rapidement.let
pour créer des variables à portée lexicale.Clojure:
defn
, selon ses documents:Identique à (def name (fn [params *] exprs *)) ou (def name (fn ([params *] exprs *) +)) avec n'importe quelle chaîne doc ou attrs ajoutée aux métadonnées var
for
: liste des compréhensionsdefmacro
: ironique?defmethod
,defmulti
: travailler avec plusieurs méthodesns
Beaucoup de ces macros facilitent l'écriture de code à un niveau plus abstrait. Je pense que les macros sont similaires, à bien des égards, à la syntaxe non-Lisps.
La bibliothèque de traçage Incanter fournit des macros pour certaines manipulations de données complexes.
la source
Les macros sont utiles pour incorporer certains modèles.
Par exemple, Common Lisp ne définit pas la
while
boucle mais possèdedo
ce qui peut être utilisé pour la définir.Voici un exemple de On Lisp .
Cela imprimera "12345678910", et si vous essayez de voir ce qui se passe avec
macroexpand-1
:Cela reviendra:
Il s'agit d'une simple macro, mais comme indiqué précédemment, elles sont généralement utilisées pour définir de nouvelles langues ou DSL, mais à partir de cet exemple simple, vous pouvez déjà essayer d'imaginer ce que vous pouvez en faire.
La
loop
macro est un bon exemple de ce que les macros peuvent faire.Common Lisp possède un autre type de macros appelé macro de lecture qui peut être utilisée pour modifier la façon dont le lecteur interprète le code, c'est-à-dire que vous pouvez les utiliser pour utiliser # {et #} a des délimiteurs comme # (et #).
la source
En voici une que j'utilise pour le débogage (dans Clojure):
J'ai dû faire face à une table de hachage roulée à la main en C ++, où la
get
méthode a pris une référence de chaîne non const comme argument, ce qui signifie que je ne peux pas l'appeler avec un littéral. Pour rendre cela plus facile à gérer, j'ai écrit quelque chose comme ceci:Bien que quelque chose comme ce problème soit peu susceptible de se produire dans lisp, je trouve particulièrement agréable que vous puissiez avoir des macros qui n'évaluent pas leurs arguments deux fois, par exemple en introduisant une vraie liaison de let. (Admis, ici j'aurais pu m'en sortir).
J'ai également recours au hack horriblement laid de l'emballage de choses dans un
do ... while (false)
tel que vous pouvez l'utiliser dans la partie alors d'un si et que la partie autre fonctionne toujours comme prévu. Vous n'avez pas besoin de cela dans lisp, qui est une fonction de macros opérant sur des arbres de syntaxe plutôt que des chaînes (ou des séquences de jetons, je pense, dans le cas de C et C ++) qui subissent ensuite une analyse.Il existe quelques macros de threading intégrées qui peuvent être utilisées pour réorganiser votre code de manière à ce qu'il soit plus clair («threading» comme dans «semer votre code ensemble», pas le parallélisme). Par exemple:
Il prend la première forme,
(range 6)
et en fait le dernier argument de la forme suivante(filter even?)
, qui à son tour devient le dernier argument de la forme suivante et ainsi de suite, de sorte que ce qui précède est réécrit enJe pense que le premier se lit beaucoup plus clairement: "prenez ces données, faites-y ceci, puis faites cela, puis faites l'autre et nous avons terminé", mais c'est subjectif; ce qui est objectivement vrai, c'est que vous lisez les opérations dans l'ordre où elles sont effectuées (en ignorant la paresse).
Il existe également une variante qui insère le formulaire précédent en tant que premier (plutôt que dernier) argument. Un cas d'utilisation est l'arithmétique:
Lit "prendre 17, soustraire 2 et diviser par 3".
En parlant d'arithmétique, vous pouvez écrire une macro qui analyse la notation infixée, de sorte que vous pourriez dire par exemple
(infix (17 - 2) / 3)
et qu'elle cracherait ce(/ (- 17 2) 3)
qui a l'inconvénient d'être moins lisible et l'avantage d'être une expression lisp valide. C'est la partie sous-langage DSL / données.la source