Common Lisp - Définition

Source: Wikipédia sous licence CC-BY-SA 3.0.
La liste des auteurs de cet article est disponible ici.

Syntaxe

Comme tout langage de la famille Lisp, Common Lisp utilise des s-expressions pour dénoter à la fois le code et certaines structures de données. Les invocations de fonctions, de formes spéciales et de macros sont écrites dans la syntaxe littérale des listes, avec le nom de la fonction (resp. forme spéciale, et macro) en première place, comme dans ces exemples :

       (+ 2 2)             ; ajoute 2 et 2, renvoie 4       (setf e 2.7182817)  ; assigne 2.7182817 à la variable e       (setq e 2.7182817)  ; assigne 2.7182817 à la variable e       (setq e 'rouge)  ; assigne le mot rouge à la variable e              NIL ; correspond a la valeur nulle        T ; correspond à la valeur booléenne true              (if (< x y)      	y ; résultat si condition true      	x) ; résultat par défault              (defun carre (x) (* x x)) ; définit une fonction qui met un nombre au carré       (carre 3)                 ; exécution de la fonction: retourne 9      

Implémentations

Standard et implémentations

Common Lisp est défini par une spécification (comme Ada et C) plutôt que par une seule implémentation (comme Perl ou Python). Il y a de nombreuses implémentations, et le standard décrit les points sur lesquels elles peuvent être divergentes pour de bonnes raisons.

De plus, les implémentations sont généralement fournies avec différents ensembles de « paquets » de bibliothèques, qui fournissent des fonctionnalités non couvertes par le standard. Certaines de ces fonctionnalités ont été introduites par la suite dans le standard, comme CLOS et la forme LOOP ; d'autres restent propres à ces implémentations. De nombreuses commodités pour le programmeur moderne -- comme l'accès aux réseaux TCP/IP -- restent hors du standard, mais sont fournies par les différentes implémentations avec parfois des différences mineures. Un processus nommé CLRFI (Common Lisp Request For Improvement), similaire aux SRFI de Scheme, a pour objectif de faire converger les fonctionnalités utiles laissées hors du standard ANSI de 1995.

Selon une erreur répandue, les implémentations de Common Lisp sont toutes des interpréteurs. En fait, la compilation fait partie de la spécification du langage. La plupart des implémentations de Common Lisp compilent les fonctions vers du code machine. D'autres compilent vers du code objet ou même vers le code d'une machine virtuelle applicative, ce qui réduit la vitesse mais améliore la portabilité.

Certaines implémentations en environnement UNIX, comme CLISP, peuvent être utilisées comme des interpréteurs de scripts (ou même comme shell).

Liste des implémentations

  • SBCL : http://www.sbcl.org]
  • Allegro Common Lisp by Franz, Inc.
  • LispWorks by LispWorks Ltd.
  • Macintosh Common Lisp by Digitool, Inc.
  • Corman Lisp by Corman Technologies
  • Scieneer Common Lisp by Scieneer Pty Ltd..

Macros

Une macro en Lisp ressemble superficiellement à une fonction. Les macros permettent au programmeur Lisp de créer de nouvelles formes syntaxiques dans le langage, par transformation de leurs paramètres représentant du code source. Par exemple, la macro suivante fournit la forme de boucle until (boucler... jusqu'à), qui est familière dans un langage comme Perl.

       (defmacro until (test &body body)         `(do ()              (, test)           ,@body))      
  • appel de la macro (utilisation dans un programme) :
       (until (= (random 10) 0)          (write-line "Hello"))      
  • première étape de macro-expansion (permet de retrouver le texte de la définition interpolé avec les paramètres de l'appel) :
       (DO NIL ((= (RANDOM 10) 0)) (WRITE-LINE "Hello"))      
  • macro-expansion complète (ce qui est exécuté pour un appel) :
       (BLOCK NIL         (LET NIL           (TAGBODY #:G7594 (IF (= (RANDOM 10) 0) (GO #:G7595)) (WRITE-LINE "hello")           (PSETQ) (GO #:G7594) #:G7595 (RETURN-FROM NIL (PROGN)))))      

Les macros ne sont donc pas évaluées à l'exécution comme les fonctions, mais au moment de la compilation. La valeur de retour d'une macro est sa macro-expansion, c’est-à-dire la transformation de code qu'elle a effectué ; c'est cela qui est évalué lors de l'exécution du programme pour chaque appel à la macro.

On peut considérer les macros comme des fonctions qui acceptent et retournent des arbres de syntaxe abstraits (les s-expressions), mais contrôlent l'évaluation de leurs paramètres. En effet dans un appel à une macro (ma-macro (+ 1 2)), l'expression (+ 1 2) n'est pas d'abord évaluée et son résultat passé en argument, elle est passée telle quelle à la macro, qui peut la réutiliser intacte, l'interpoler avec d'autres fragments de code ou la transformer plus ou moins complètement.

Comme les fonctions, les macros peuvent utiliser l'ensemble du langage Common Lisp (et bibliothèques tierces) pour effectuer leur travail de transformation (on les appelle pour cela des macros procédurales), contrairement aux macros du langage C qui ne permettent que des substitutions de chaînes de caractères au niveau du source, sans accès à l'ensemble du langage C lui-même.

Langages embarqués

L'intérêt principal des macros ne réside pas dans les petits utilitaires comme l'exemple ci-dessus, dont la prolifération dans les programmes Lisp peut conduire à un effet d'obfuscation, mais dans la possibilité de définir des langages embarqués dans Lisp, qui ciblent un domaine applicatif particulier. Un exemple classique est l'inclusion dans le langage d'une extension permettant de faire de la programmation logique à la manière de Prolog ou encore de la programmation par contrainte (ajout d'un nouveau paradigme de programmation). Il est possible en effet de construire, en utilisant des macros et des fonctions Lisp, un compilateur pour un langage de plus haut niveau que le langage de base mais qui reste intégré dans ce dernier.

Tous les domaines applicatifs peuvent bénéficier de la technique consistant à écrire un programme en ayant une approche descendante (top-down), dans laquelle le problème à résoudre est décomposé jusqu'à pouvoir être exprimé dans les termes du langage de base, et une approche montante (bottom-up), où l'on étend le langage de base avec des concepts facilitant l'expression du problème. Aucun langage de programmation à usage général ne pouvant être fourni avec les opérateurs spécifiques à une infinité de domaines d'applications spécifiques, la faculté de pouvoir construire un langage adapté (l'approche bottom-up), par extension et réutilisation de la base, est un avantage majeur de Lisp sur les autres langages universels. Les macros sont de ce point de vue un avantage unique des langages de la famille Lisp. La facilité d'écriture des macros tient à l'utilisation des s-expressions pour la syntaxe de Lisp.

Templates pour le code

Le mécanisme des macros ne serait pas assez commode à l'usage sans un mécanisme permettant de représenter du code Lisp sous forme de patron (modèle, ou encore template) dans lequel des éléments calculés ou donnés en paramètre peuvent être injectés. Common Lisp offre la quasiquotation, représentée par le caractère ` (dit backquote). Bien que l'opérateur backquote puisse être utilisé dans des fonctions, c'est dans les macros que son usage s'avère primordial : il permet d'améliorer la lisibilité du code produit par une macro dans des proportions considérables.

Dans l'exemple ci-dessus, les backquotes ont été utilisées pour calculer le résultat. Le corps de la macro until est entièrement représenté par un template de code, utilisant l'opérateur d'itérations do. Rappelons que DO accepte trois arguments : un ensemble de variables d'itérations définies localement et itérées en parallèle, un ensemble de clauses permettant de définir les conditions d'itération des variables et d'arrêt de la boucle, et un groupe - de taille quelconque - d'opérations ou d'actions arbitraires :

       do ({var | (var [init-form [[step-form]])}*) (end-test-form result-form*) declaration* {tag | statement}*        => result*      

Pour la boucle until, l'utilisation de DO est simple : il n'y a pas de variables d'itération, il y a exactement une clause de test, et on peut admettre un groupe d'opération à effectuer à chaque itération. Le template à produire reflète cela :

       `(do ()            (, test)         ,@body))      

On note la backquote précédant l'expression DO. Dans une expression backquotée, on peut injecter des informations de deux manières : avec l'opérateur , (unquote) qui injecte l'objet immédiatement à sa droite, ou l'opérateur , @ (splice-unquote) qui admet une liste à sa droite et injecte sur place le contenu de la liste.

Le résultat de :

       (macroexpand-1 '(until (= (random 10) 0)                           (write-line "Hello")))      

est l'expansion

      (DO NIL ((= (RANDOM 10) 0)) (WRITE-LINE "Hello"))      

dans laquelle on retrouve le template combiné avec les paramètres de l'appel à la macro. L'expansion finale, produite par (macroexpand ...), contient l'expansion de l'opérateur DO lui-même en termes d'opérateurs de plus bas niveau. Comme DO est un opérateur défini par le langage Common Lisp, la sémantique de l'opérateur est toujours la même. Mais il se peut que cette expansion varie d'un compilateur à l'autre. On voit par là que les macros sont déjà massivement utilisées dans l'implémentation d'un langage riche et complexe comme Common Lisp.

Capture de variable

Les macros Common Lisp sont capables de capture de variable, une situation où des symboles situés dans le corps de la macro-expansion coincident avec des symboles du contexte appelant. Pour cette raison elles sont parfois appelées macro « non hygiéniques », par comparaison avec le système de « macro hygiéniques » de Scheme, qui garantit la séparation entre ces ensembles de symboles.

La capture de variable est parfois un effet désiré ; lorsque ce n'est pas le cas, elle doit être évité par l'emploi de gensyms, ou symboles dont l'unicité est garantie.

Page générée en 0.133 seconde(s) - site hébergé chez Contabo
Ce site fait l'objet d'une déclaration à la CNIL sous le numéro de dossier 1037632
A propos - Informations légales
Version anglaise | Version allemande | Version espagnole | Version portugaise