Common lisp object system



Image manquante
WikiLettreMini.png
Image:WikiLettreMini.png

Cette page est considérée comme un article à approfondir.


Image manquante
Langage_progr.png
image:Langage_progr.png

Cet article fait partie de la série
Langages de programmation
Langages à objets
Ada 95 - C++ - C#
Common Lisp
Delphi - Eiffel - Java
Nice - Oz - Python
Simula - Smalltalk
Visual Basic
Langages impératifs
APL - ASP - Assembleur
BASIC - C - COBOL
Forth - FORTRAN - Logo
Pascal - Perl - PHP
Langages fonctionnels
Erlang - Haskell
ML/OCaml - Oz
Lisp/Common Lisp
Scheme
Langages déclaratifs
Clips - Oz - Prolog
Langages concurrents
Ada 95 - Erlang
Java - Oz
Langages balisés
HTML - SGML - XML
Dialectes XML
S-expressions
Voir aussi
Conception - Codage
Tests - Optimisations

Le Common Lisp Object System, souvent abrégé en CLOS (prononcé 'si-lauss'), est l'ensemble des primitives présentes dans le langage de programmation Common Lisp pour construire un programme orienté objet. Il existe également une version de CLOS pour le langage Scheme, nommée TinyClos.

Sommaire

Présentation

CLOS est un système objet à classes (il existe des systèmes à prototypes). Il descend de flavors et common loops, développés dans les années 1980. Common Lisp a été le premier langage à objet standardisé par l'ANSI, en 1995, précédant de peu SmallTalk.

Les objets et Lisp

D'un certain point de vue, Lisp est un langage orienté objet depuis le début : les structures de données manipulées ont une identité et sont réflexives. La seule chose qui lui manquait pour recevoir l'estampille orienté objet, c'est la capacité d'étendre le système de types de Lisp.

Caractéristiques

Primitives

La plus petite définition de classe

(defclass foo () ())  ;; équivalent de (defclass foo (Object) ())
 

Par défaut, une classe est un sous-type de Object. Les types par défaut de Common Lisp (nombres, chaînes, hashages, vecteurs, etc.) ne sont pas dérivables.

Créer une instance

(make-instance 'foo)
 

Attributs : définition, initialiseur, accesseurs, allocation

À partir d'un exemple plus intéressant, on montre les principales caractéristiques :

(defclass graphe ()
    ((noeuds :accessor noeuds :initform (make-hash-table))
     (arcs   :accessor arcs   :initform (make-hash-table)))
 
(defclass noeud ()
    ((data     :accessor data     :initarg :data)
     (in-arcs  :accessor in-arcs  :initform (make-hash-table))
     (out-arcs :accessor out-arcs :initform (make-hash-table)))
 
(defclass arc ()
    ((in-noeud  :accessor in-noeud  :initarg :in-noeud)
     (out-noeud :accessor out-noeud :initarg :out-noeud)))
 

Les attributs d'une classe sont nommés slots dans la terminologie de CLOS. Chaque slot est défini par une liste contenant au minimum son nom, en première position. Des accesseurs en lecture seule (:reader) ou lecture-écriture (:accessor ou la combinaison de :reader et :writer) peuvent être spécifiés, ainsi que la valeur initiale (:initform) et un nom pour donner une valeur au moment de l'instanciation. Ainsi, pour construire un nœud doté d'une boucle, on peut écrire :

(defparameter *un-noeud* (make-instance 'noeud))
 (defparameter *un-arc*   (make-instance 'arc 
                                         :in-noeud *un-noeud*
                                         :out-noeud *un-noeud*))
 
(setf (gethash (in-arcs *un-noeud*) *un-arc*) *un-arc*)
 (setf (gethash (out-arcs *un-noeud*) *un-arc*) *un-arc*)
 

Ces manipulations de tables de hashage étant lourdes, on se construit un opérateur sethash pour en racourcir l'expression :

(defmacro sethash (key value hash)
   `(setf (gethash ,key ,hash) ,value))
 

Ainsi armé, on peut réécrire les deux lignes précédents :

(sethash (in-arcs *un-noeud*) *un-arc*)
 (sethash (out-arcs *un-noeud*) *un-arc*)
 

Méthodes

Voici la plus grande originalité de CLOS : les méthodes n'y sont pas définies sur une classe. Au lieu de cela, elles sont définies un tuple de classes. Par exemple, on veut simplifier la connexion des nœuds et des arcs.

Sélection simple

Dans un langage à sélection simple, il faudrait attacher la méthode connecter à la classe nœud OU à la classe arrête, pour pouvoir écrire :

un_arc.connecter (un_noeud, un_autre_noeud)
 

Ou encore :

un_noeud.connecter (un_arc, un_autre_noeud)
 

Une méthode a un paramètre privilégié, son receveur (au sens de la réception d'un message). La notation pointée souligne ce privilège.

Sélection multiple

Avec la sélection multiple de la méthode sur le tuple des arguments obligatoires, on n'a plus besoin de choisir :

(defgeneric connecter ((noeud noeud arc))
 

Une fonction générique est un sac de méthodes. On y place la première :

(defmethod connecter ((noeud-1 noeud) (noeud-2 noeud) (un-arc arc))
     (setf (in-noeud un-arc) noeud-1)
     (setf (out-noeud un-arc) noeud-2)
     (sethash out-arcs noeud-1 un-arc)
     (sethash in-arcs noeud-2 un-arc))
 

On peut maintenant écrire, pour construire la boucle :

(connecter *un-noeud* *un-noeud* *un-arc*)
 


(... à continuer ...)

See also: Common lisp object system, 1995, ANSI, APL (langage), Active server pages, Ada (langage), Années 1980, Assembleur (langage), Basic