Essai de traduction de
 Fundamentals of CLOS
 Nick Levine, Ravenbrook Limited, 2003-07-15
 http://cl-cookbook.sourceforge.net/clos-tutorial/

 OS utilisé par le traducteur: Linux Debian Wheezy/Jessie
 Versions de lisp utilisées par le traducteur:
 clisp par Bruno Haible et Sam Steingold (free)
 SBCL Steel Bank Common Lisp (free) avec entre autres le package
 cl-closer-mop Cross Implementation AMOP library installé

 1 Introduction
 ==============

 Ce document a été écrit pour la présentation lors d'une session tutoriel
 à la Conférence internationale Lisp tenue à New York en Octobre 2003

 Ce tutoriel vise un public ayant une connaissance de base de Lisp ou
 scheme, et qui veut apprendre quelque chose sur la façon d'utiliser le
 "Common Lisp Object System" (CLOS). Cependant, dans une tentative de
 fournir quelque chose pour tout le monde, le tutoriel couvrira:

 -o- une introduction à 10% de CLOS dont vous avez besoin pour couvrir
     90% des cas d'utilisation;
 -o- un certain nombre de déclarations dogmatiques, par exemple sur les
     différences entre les autres systèmes d'objets et CLOS;
 -o- un bref regard "sous le capot" comment certaines des fonctionnalités
     les plus dynamiques de CLOS pourrait être implémentées;
 -o- quelques exercices

 L'auteur a travaillé sur le projet de LispWorks au Harlequin pendant dix ans.
 Depuis, il a enseigné en premier cycle Lisp, écrit un moteur de recherche open
 source, et repris l'existence sans stress d'un consultant en informatique.

 Les exemples de ce tutoriel sont disponibles dans un fichier séparé
 http://cl-cookbook.sourceforge.net/clos-tutorial/examples.lisp.
 J'espère utiliser le code dans
 http://cl-cookbook.sourceforge.net/clos-tutorial/present.lisp
 pendant le tutoriel.

 Ce document n'est pas confidentiel. Il est disponible sur le web, au
 http://cl-cookbook.sourceforge.net/clos-tutorial/

 2 Historique
 ============
 CLOS (se prononce soit en une syllabe rimant avec "dross", ou deux syllabes
 comme dans "see-loss") est le "Common Lisp Object System"
 La fonctionnalité appartenant à ce nom a été ajouté au langage Common Lisp
 entre la publication de la première édition de Steele de "Common Lisp, le
 langage" en 1984 et l'officialisation du langage comme norme ANSI dix ans
 plus tard.
 Le matériau de base pour CLOS était un rapport écrit en trois chapitres.
 Les deux premiers, composés de "Programmer Interface Concepts" et
 "Functions in the Programmer Interface", vont maintenant être trouvés dans
 les deux moitiés du chapitre 28 dans [Steele 1990] et ont été la base pour
 les parties pertinentes de la spécification ANSI lorsque lorsqu'elles sont
 apparues plus tard. Le troisième chapitre, sur le protocole MetaObject,
 était considéré (par ses auteurs, je crois) comme incomplet et jamais publié.

 Ce tutoriel est également incomplet, mais dans un sens différent: j'ai
 délibérément omis autant que je le pouvais. CLOS offre de nombreux styles
 alternatifs de travail, ainsi que - en détails assez important - un certain
 nombre de possibilités pour les applications d'étendre et de reconfigurer
 CLOS lui-même. (Certains de ces détail seront couverts dans le tutoriel
 suivant, sur le "Protocole MetaObject") Le but de ce tutoriel est de fournir
 une masse suffisante dans les (par exemple) 10% de CLOS qui couvre 90% des
 cas d'utilisation - assez pour qu'un débutant puisse s'en sortir. Nous allons
 accéder à ce 10% en examinant dans certains (mais pas tous!) détails deux
 macros: defclass et defmethod.

 Quant à dire ce qu'un système d'objet est: Je peux seulement dire qu'à la fin
 de ce tutoriel, vous devriez avoir une idée de ce que CLOS a à offrir.
 (Est-ce juste une collection de 33 fonctions, huit macros et quelques nouveaux
 types intéressants? Ou est-ce quelque chose de plus profond?)
 Historiquement, et par leur implémentation, tous les concepts ici sont fortement
 liés. Cependant, quand vous les utiliserez vous-même vous verrez que dans
 une application la relation n'est pas si forte, et vous pourrez choisir ce qui
 est utile pour vous et laisser de coté ce qui ne l'est pas.

 2.1. Références
 °°°°°°°°°°°°°°°
 Elles sont énumérées dans l'annexe A à la fin de ce document.

 2.2. Pour commencer
 °°°°°°°°°°°°°°°°°°°
 Théoriquement, cela ne devrait pas être un problème dans tout Common Lisp
 entièrement activé, parce que CLOS fait partie du langage. Cependant,
 certaines implémentations peuvent s'attendre à un require "clos" ou
 quelque chose de similaire. Consultez votre manuel.

 La plupart des exemples de ce tutoriel devrait fonctionner correctement dans
 un paquet qui "utilise" Common-Lisp, et ils devraient être porté dans toute
 implémentation conforme. Les exceptions sont marqués comme telles. J'ai testé
 les exemples dans la version 4.2.7 (LispWorks, utilisés pour générer tous
 les exemples ci-dessous) et Allegro CL (version 6.2).

 NDT: dans wheezy Debian avec sbcl il faut installer le package cl-closer-mop

 3. Classes et instances
 =======================

 3.1. Rappel - l'approche non-OO
 °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
 Avant de vous présenter la première de nos deux macros, passons en revue sa
 non-orientée objet équivalent: defstruct. Le langage vous fournit un certain
 nombre de types de données spécialisées (cons, string, hash-table, etc.)
 ainsi que ce mécanisme pour définir vos propres structures. Un exemple:

 (defstruct point x y z)

 La forme est à peu près équivalente à une déclaration struct en C. Il définit un
 nouveau type, appelé le point, avec trois slots (ou champs si c'est un mot
 qui ne vous plait pas) appelés x, y et z.

 Globalement, l'invocation ci-dessus à defstruct nous donne tous les éléments suivants:

 -o- Une fonction constructeur make-point, qui prend des arguments mot-clé:
     :x :y et :z (tous mis par défaut à nil si non fourni). Chaque fois que
     vous appelez cette fonction un nouveau point est alloué et retourné.
 -o- Tout objet retourné par make-point sera de type point, et répondra avec
     enthousiasme au prédicat point-p.
 -o- Les setfable accesseurs point-x, point-y et points-z peuvent être utilisés
     pour lire et modifier les emplacements de tout objet point.
 -o- Un copieur superficiel, copy-point.

 Les structures peuvent avoir un nombre quelconque de slots, y compris zéro et
 - avec des listes et des vecteurs - les slots peuvent avoir des valeurs.

 Dans cet exemple, notez la forme dans laquelle les structures sont affichées par
 défaut, et qui peut être analysé par le reader Lisp.

 CL-USER 1 >  (defstruct point x y z)
POINT

 Cl-USER 2 > (defun distance-from-origin (pt)
              (let* ((x (point-x pt))
                     (y (point-y pt))
                     (z (point-z pt)))
                (sqrt (+ (* x x) (* y y) (* z z)))))
DISTANCE-FROM-ORIGIN

 CL-USER 3 > (defun reflect-in-y-axis (pt)
              (setf (point-y pt)
                    (- (point-y pt))))
REFLECT-IN-Y-AXIS

 CL-USER 4 >  (setf my-point (make-point :x 3 :y 4 :z 12))
#S(POINT X 3 Y 4 Z 12)

 CL-USER 5 > (type-of my-point)
POINT

 CL-USER 6 > (distance-from-origin my-point)
13

 CL-USER 7 > (reflect-in-y-axis my-point)
-4

 CL-USER 8 > my-point
#S(POINT X 3 Y -4 Z 12)

 CL-USER 9 > (setf a-similar-point #s(point :x 3 :y -4 :z 12))
#S(POINT X 3 Y -4 Z 12)

 CL-USER 10 > (equal my-point a-similar-point)
NIL

 CL-USER 11 > (equalp my-point a-similar-point)
T

 Notez que defstruct a un certain nombre d'options (dont nous ne parlerons pas ici)
 pour décrire l'héritage, le comportement de l'impression, les types de slots et
 les valeurs par défaut, et ainsi de suite.

 3.2. Introduisons la macro defclass
 °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
 La macro utilisé pour définir de nouveaux types de données dans CLOS est
 defclass. Un exemple:

 (defclass point () (x y z))

 On ignore les parenthèses vides pour le moment. Notez également les
 parenthèses autour de l'ensemble des noms des slots (contrairement defstruct).
 L'invocation ci-dessus nous donne ce qui suit (et pas plus):

 -o- Un type CLOS (ou classe) nommé point.
 -o- Trois slots dans cette classe, nommées encore x, y et z.

 Notez que - contrairement à defstruct ci-dessus - defclass ne nous donne aucun
 des objets suivants: constructeur, prédicat, accesseurs (à moins que nous le
 demandions pour eux explicitement - voir la section 3.5 ci-dessous),
 copier, #s print/read syntax

 Vous pouvez générer des fonctionnalités similaires dans CLOS, mais cela ne
 se fait pas automatiquement comme cela se fait avec les structures. Très
 souvent, vous verrez que vous n'avez pas besoin de la puissance de CLOS et
 que defstruct est plus que suffisant pour répondre à vos besoins.
 Quand j'écris une application, je commence généralement par définir mes
 types avec defstruct, et je ne les change pour defclass que lorsqu'il
 devient nécessaire de le faire.

 Notez ensuite que si un type a déjà été défini comme une structure,
 alors vous ne pouvez pas le redéfinir en tant que classe.
 (D'autre part, "les conséquences de la redéfinition d'une structure de
 defstruct sont imprévues") Nous allons "uninterning"  la structure
 point dans cette session pour éliminer le nom de l'ancien type:

 CL-USER 12 > (unintern 'point)
T

 CL-USER 13 > (defclass point () (x y z))
#<STANDARD-CLASS POINT>

 CL-USER 14 > (setf my-point (make-instance 'point))
#<POINT #x0003345B2518>

 CL-USER 15 > (type-of my-point)
POINT

 CL-USER 16 > (defun set-point-values (pt a b c)
               (setf (slot-value pt 'x) a
                     (slot-value pt 'y) b
                     (slot-value pt 'z) c))
SET-POINT-VALUES

 CL-USER 17 > (set-point-values my-point 3 4 12)
12

 CL-USER 18 > (defun distance-from-origin (pt)
               (with-slots (x y z) pt
                  (sqrt (+ (* x x) (* y y) (* z z)))))
DISTANCE-FROM-ORIGIN

 CL-USER 19 > (distance-from-origin my-point)
13

 Notez les points suivants:
 -o- L'utilisation de make-instance par exemple pour allouer une instance
     de notre nouvelle classe.
 -o- La représentation imprimée "illisible" de my-point.
 -o- La fonction setfable slot-value utilisée pour accéder aux valeurs dans
     les slots d'une instance.
 -o- La macro with-slots, pour abréger les appels à slot-value. Le premier
     argument est une liste de noms de slots. Le deuxième argument évalue
     une instance CLOS; ceci est suivi par des déclarations facultatives et
     un progn implicite. Lexicalement au cours de l'évaluation du corps, un
     accès à l'un de ces noms de variable est équivalente à accéder au slot
     correspondant de l'instance CLOS.

 Exercice: Réécrire set-point-values en utilisant with-slots.

 Exercice: Utilisez symbol-macrolet pour implémenter with-slots. Notez que
 chaque nom figurant dans le premier argument de symbol-macrolet peut être
 remplacé par la paire (variable-name slot-name).

 Exercice: Ecrire une defclass-plus macro qui s'étend dans un defclass
 ainsi que tout ou partie de ce qui suit, dans l'esprit de defstruct:
 constructeur, prédicat, accesseurs et copieur. Cela peut être fastidieux,
 dans ce cas persuadez-vous que vous savez ce que vous êtes en train de
 faire, puis arrêtez.

 3.3.Les classes sont aussi des instances
 °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
 Comparer les valeurs renvoyées de l'exemple appelle à defstruct
 et defclass. Defstruct ne retourne pas quelque chose d'utile,
 mais defclass a retourné un objet lisp de la forme: # <STANDARD CLASSE POINT>.
 Cet objet est la classe nommée point. C'est un objet de première
 classe dans Lisp: un mode de réalisation d'un type CLOS. En fait,
 il peut être passé comme argument de type à typep et subtypep.
 C'est aussi un objet CLOS, ce qui signifie qu'il doit être une
 instance d'une classe CLOS, et nous pouvons savoir ce que cette
 classe est, comme dans l'exemple ci-dessous.

 CL-USER 20 > (find-class 'point)
#<STANDARD-CLASS POINT>

 CL-USER 21 > (class-name (find-class 'point))
POINT

 CL-USER 22 > (class-of my-point)
#<STANDARD-CLASS POINT>

 CL-USER 23 > (typep my-point (class-of my-point))
T

 CL-USER 24 > (class-of (class-of my-point))
#<STANDARD-CLASS STANDARD-CLASS>

Le dernier exemple a l'air un peu rébarbatif de prime abord.
L'objet my-point est une instance de la classe nommée point;
la classe nommée point est elle-meme une instance de la classe
nommée standard-class. Nous disons que la classe nommée standard-class
est la métaclasse (i.e. la classe de la classe) de my-point.

Notation:
décrire quelque chose comme "la classe nommée standard-class" peut
être correct, mais cela n'est pas très élégant à lire. Quand nous
parlons de "la classe standard-class" ou même de standard-class,
nous entendons généralement la classe nommée par ce symbol.

 3.4. Vous n'avez pas besoin d'objets CLOS pour utiliser CLOS
 °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
 Généreusement, les fonctions introduites dans la dernière section
 travaillent également sur des objets Lisp qui ne sont pas des instances
 de CLOS:

 CL-USER 25 > (let ((the-symbol-class (find-class 'symbol)))
               (values the-symbol-class
		       (class-name the-symbol-class)
                       (eq the-symbol-class (class-of 'symbol))
		       (class-of the-symbol-class)))

#<BUILT-IN-CLASS SYMBOL> ;
SYMBOL
T ;
#<STANDARD-CLASS BUILT-IN-CLASS 20306414>

 Reportant à la section 4.5 la question de savoir pourquoi cela pourrait
 nous être utile, nous voyons ici que les symboles Lisp sont des instances
 de la classe du systeme symbol.
 C'est l'un des 75 cas dans lesquels le langage nécessite qu'une classe
 existe avec le même nom que le type Lisp correspondant.
 Beaucoup de ces cas sont concernés par CLOS lui-même (par exemple, la
 correspondance entre le type standard-classe et la classe CLOS de ce nom)
 ou avec le système de l'état (ce qui pourrait ou ne pourrait pas être
 construit en utilisant des classes CLOS dans n'importe quelle application
 donnée). Cependant, 33 correspondances subsistent quant aux types Lisp
 «traditionnels»:

 array		 	 hash-table	   readtable
 bit-vector		 integer	   real
 broadcast-stream 	 list		   sequence
 character		 logical-pathname  stream
 complex	 	 null		   string
 concatenated-stream  	 number		   string-stream
 cons			 package	   symbol
 echo-stream		 pathname	   synonym-stream
 file-stream		 random-state	   t
 float		 	 ratio		   two-way-stream
 function		 rational	   vector

 Remarquez que tous les types lisp "traditionnel" ne sont pas inclus dans
 cette liste.
 (Considérez: atom, fixnum, short-float, et n'importe quel type, non désigné
 par un symbol)

 La présence de t est intéressante. Tout comme chaque objet Lisp est de
 type t, chaque objet Lisp est aussi un membre de la classe nommee t.
 C'est un exemple simple de l'appartenance à plus d'une classe à la fois, et
 il remet en question le problème de l'héritage, que nous allons examiner en
 détail plus tard (section 3.6).

 CL-USER 26 > (find-class t)

 #<BUILT-IN-CLASS T>

 En plus des classes correspondant aux types Lisp, il ya aussi une classe CLOS
 pour chaque type de structure que vous définissez:

 CL-USER 27 > (defstruct foo)

FOO

 CL-USER 28 > (class-of (make-foo))

#<STRUCTURE-CLASS FOO>

 La méta-classe d'une structure-objet est la classe structure-class.
 Il est dépendant de l'implémentation si la métaclasse d'un objet Lisp
 "traditionnelle" est standard-class (comme dans la section 3.3),
 structure-class ou built-in-class. Restrictions:

---------------------------------------------------------------------------------
 built-in-class | Ne peut pas utiliser make-instance,
		| ne peut pas utiliser slot-value,
		| ne peut pas utiliser defclass pour modifier,
		| ne peut pas créer des sous-classes.
---------------------------------------------------------------------------------
structure-class | Ne peut pas utiliser make-instance,
		| pourrait fonctionner avec slot-value (implementation-dependent).
		| Use defstruct to subclass application structure types.
		| les conséquences de modifier une structure de classe existante
		| sont undefinies: recompilation complète peut être nécessaire.
---------------------------------------------------------------------------------
standard-class  | Aucune de ces restrictions.
---------------------------------------------------------------------------------

 3.5. Slots
 °°°°°°°°°°
 La syntaxe complète de defclass est:

 defclass class-name ({superclass-name}*) ({slot-specifier}*) [[class-option]]

 Nous discuterons du deuxième argument dans la section 3.6 ci-dessous. classe-option
 est en dehors du cadre de ce tutoriel. Dans cette section, nous allons jeter un regard
 sur les slot-specifers.
 Dans la définition de la classe point ci-dessus, chaque slot a été specifié simplement
 par son nom. Nous pouvons à la place spécifier les slots ainsi:

 (slot-name [[slot-option]])

 Chaque slot-option se compose d'un mot-clé suivi d'une valeur.

 Parmi les mots-clés disponibles sont les suivants; vous pouvez en spécifier
 autant que vous en avez besoin.

 :accessor
 Définit les méthodes (voir la section 4 ci-dessous, les considérer comme des
 fonctions pour le moment), nommées par la valeur donnée, pour la lecture
 et la modification du slot.

 :reader
 Définit une méthode unique pour la lecture du slot en lecture seule contrepartie
 de :accesseurs.

 :initarg
 Indique un mot-clef qui peut être utilisé pour transmettre une valeur initiale
 à ce slot pour make-instance (un argument d'initialisation).

 :writer
 Définit une méthode unique pour l'écriture du slot contrepartie de :accesseurs.

 :initform
 Indique une valeur par défaut pour ce slot, pour être utilisé si aucune valeur
 initiale a ete explicitement spécifie. Cette forme est evaluée chaque fois qu'il
 est nécessaire, dans l'environnement lexical du defclass.

 :allocation
 Indique si la valeur de cet emplacement:
      -1- peut être différent pour chaque instance de la classe
	  (:allocation :instance - par défaut - résultant dans
	  un slot local); ou
      -2- est partagée entre toutes les instances de la classe
	  (:allocation :class - resulting in a class slot).

 Dans l'exemple suivant, notez les points suivants:

 -1- la spécification et l'utilisation de l'argument :x, initialisation pour le slot x
 -2- la valeur par defaut pour le slot y
 -3- comment modifier la valeur de la classe du slot z - mais pas les slots locaux -
     affecte toutes les instances de la classe (que ces instances existent encore ou non)
 -4- la différence de style entre l'utilisation d'un accesseur (daft-y) et de la slot-value

 CL-USER 29 > (defclass daft-point ()
    ((x :accessor daft-x :initarg :x)
     (y :accessor daft-y :initform 3.14159)
     (z :reader daft-z :allocation :class)))

#<STANDARD-CLASS DAFT-POINT>

 CL-USER 30 > (setf (slot-value (make-instance 'daft-point) 'z) 42)
42

  CL-USER 31 >(setf my-daft-point (make-instance 'daft-point :x 19))
 #<DAFT-POINT #x0003345D4368>

 CL-USER 32 > (list (daft-x my-daft-point) (daft-y my-daft-point) (daft-z my-daft-point))
(19 3.14159 42)

 CL-USER 33 > (let ((temp (make-instance 'daft-point)))
               (setf (daft-y temp) 999
	             (slot-value temp 'z) 0))
 0

 CL-USER 34 > (list (daft-x my-daft-point) (daft-y my-daft-point) (daft-z my-daft-point))
 (19 3.14159 0)

 3.6. Sous-classes et héritage
 °°°°°°°°°°°°°°°°°°°°°°°°°°°°°
 Supposons que nous voulions deux classes qui aient le même comportement,
 en ce sens que l'une d'elle (la sous-classe) soit définie en termes de
 l'autre (la superclasse). Cela nous amène à la notion d'héritage, dans
 une certaine forme commune à tous les systèmes d'objets.

 Par exemple:

 CL-USER 35 > (defclass animal ()
       ((legs :reader leg-count
	      :initarg :legs)
        (comes-from :reader comes-from
		    :initarg :comes-from)))

#<STANDARD-CLASS ANIMAL>

 CL-USER 36 > (defclass mammal (animal)
      ((diet :initform 'antelopes
             :initarg :diet)))

#<STANDARD-CLASS MAMMAL>

 CL-USER 37 > (defclass aardvark (mammal) ;;oryctérope
          ((cute-p :accessor cute-p
       	     	   :initform nil)))

#<STANDARD-CLASS AARDVARK>

 Dans cet exemple, mammal est definie pour être une sous-classe de animal.
 Cela signifie que chaque instance de mammal est une instance de animal.
 Si nous faisons (make-instance 'mammal), nous obtenons un objet avec trois
 slots: diet qui vient directement de la definition de mammal, plus legs et
 comes-from tous les deux hérités de la definition de animal.
 De même, chaque aardvark est à la fois un mammal et un animal, et a quatre
 slots, dont trois sont hérités de super-classes. Notez que la relation de
 sous-classe est transitive - aardvak est une sous-classe (indirecte) de
 animal, par l'intermédiaire de mammal. Par conséquent, vous n'avez pas besoin
 d'indiquer explicitement animal comme une superclasse de aardvark.

					-------------------
					|  animal 2 slots |
					-------------------
						|
						|
						v
			    ------------------------------------------
			    | mammal 1 slot direct + 2 slots herites |
			    ------------------------------------------
						|
						|
						|
						v
			  --------------------------------------------
			  | aardvark 1 slot direct + 3 slots herites |
			  --------------------------------------------

	figure 1. aardvark est une sous-classe de mammal, qui est une
		  sous-classe de animal.
		  Les flèches indiquent la relation "X est superclasse de Y".

 Dans CLOS, ces relations peuvent être interrogées par les readers
 class-direct-superclasses et class-precedence-list.

 Note d'implémentation:
 ces deux fonctions ne font pas partie de Common Lisp. Dans Lispworks elles sont
 disponibles via votre package-use-list, dans Allegro elles sont exportées de
 ACLMOP. Toujours dans Allegro, vous devez avoir fait une instance de aardvark
 avant de pouvoir interroger sa liste de precedence.)

 NDT: Ces fonctions font aussi partie de clisp.
      Avec SBCL sous Debian wheezy/jessie le package  cl-closer-mop doit être installé:

 CL-USER 38 > (class-direct-superclasses (find-class 'aardvark))

(#<STANDARD-CLASS MAMMAL>)

 CL-USER 39 > (class-precedence-list (find-class 'aardvark))

(#<STANDARD-CLASS AARDVARK> #<STANDARD-CLASS MAMMAL> #<STANDARD-CLASS ANIMAL>
#<STANDARD-CLASS STANDARD-OBJECT> #<BUILT-IN-CLASS T>)

 NDT: avec SBCL (sb-mop:class-direct-superclasses (find-class 'aardvark))
		(sb-mop:class-precedence-list (find-class 'aardvark))

 La class-precedence-list d'une classe est une liste qui commence à partir de cette
 classe et de manière récursive montre les superclasses, dans l'ordre. Les trois
 premiers éléments dans la liste ci-dessus ne sont pas surprenants, mais les deux
 autres méritent une brève discussion.

 -1- Tous les objets CLOS (l'appel de make-instance provoque une allocaion)
     sont des instances de la norme-objet de classe du système. En d'autres
     termes, toutes les instances de standards-class héritent de standard-objet.
     Vous n'avez jamais à énumérer standard-objet comme une superclasse parce
     que c'est fait implicitement.
     (defclass foo () ()) et (defclass foo (standard-objet) ()) sont identiques.

 -2- Toutes les classes sont sous-classes de la classe nommée t, que nous avons
     présentée dans la section 3.4 ci-dessus.

					  -----
					  | t |
					  -----
					    |
					    |
					    v
				    ------------------
				    | standard-objet |
				    ------------------
					    |
					    |
					    v
					----------
					| animal |
					----------
					    |
					    |
					    v
					----------
					| mammal |
					----------
					    |
					    |
					    v
					------------
					| aardvark |
					------------

	figure 2 Précédence des classes pour aardvark

 Maintenant considérons:

 CL-USER 40 > (defclass figurine ()
        ((potter :accessor made-by :initarg :made-by)
         (comes-from :initarg :made-in)))

#<STANDARD-CLASS FIGURINE>

 CL-USER 41 > (defclass figurine-aardvark (aardvark figurine)
         ((name :reader aardvark-name
		:initarg :aardvark-name)
          (diet :initform nil)))

#<STANDARD-CLASS FIGURINE-AARDVARK>

 La classe figurine-aardvark ici hérite son comportement de deux super-classes
 directes. Toute instance de cette classe sera donc aussi une instance de
 chacune de ces deux classes, et de toutes les super-classes.

							   -----
					  		   | t |
					  		   -----
							     |
							     |
							     v
						    ------------------
				    	    --------| standard-objet |--------
					    |	    ------------------	     |
					    |				     |
					    |				     |
					    v				     |
					----------			     |
					| animal |			     |
					----------			     |
					    |				     |
					    |				     |
					    v				     |
					----------			     |
					| mammal |			     |
					----------			     |
					    |				     |
					    |				     |
					    v				     v
					------------			 ------------
					| aardvark |			 | figurine |
					------------			 ------------
					    |	    			     |
					    |	    ---------------------    |
					    ------->| aardvark-figurine |<----
						    ---------------------

			figure 3. figurine-aardvark hérite de deux superclasses directes


 Cela s'appelle l'héritage multiple. C'est une fonctionnalité terriblement
 utile de CLOS. Tous les systèmes OO n'ont pas cette propriété.
 Par exemple, considérez les outils en Java, ou vous pouvez avoir l'héritage
 complet de pas plus d'une superclasse et une forme très limitée de l'héritage
 de toutes les autres. L'héritage multiple dans CLOS est symétrique entre
 autant de super-classes que vous voulez spécifier. Assurez-vous que le système
 de OO vous utilisez prend en charge l'héritage multiple complet.

 Notez que, parce que chaque classe CLOS hérite de la norme-objet, une
 caractéristique de l'héritage multiple est la présence de "boucles" dans le
 diagramme d'héritage de classe.
 Le calcul de la liste des précédences n'est pas simple (voir tri topologique),
 mais il est bon de savoir que le résultat doit être compatible avec:
 (a) l'ordre de superclasses explicitement nommées.
 (b) les listes de précédence de la classe de toutes les classes parentes.

 CL-USER 42 > (class-precedence-list (find-class 'figurine-aardvark))

	(#<STANDARD-CLASS FIGURINE-AARDVARK> #<STANDARD-CLASS AARDVARK>
	#<STANDARD-CLASS MAMMAL> #<STANDARD-CLASS ANIMAL>
	#<STANDARD-CLASS FIGURINE> #<STANDARD-CLASS STANDARD-OBJECT>
	#<BUILT-IN-CLASS T>)

 Regardons maintenant les slots de figurine-aardvark:

    legs - hérité de animal;
    comes-from - herité de animal et figurine;
    diet - hérité de mammal, aussi un slot direct de figurine-aardvark;
    cute-p - hérité de aardvark;
    potter - hérité de figurine;
    name - slot direct de figurine-aardvark.

 Qu'advient-il si un slot avec un certain nom donné apparaît plusieurs fois
 dans la liste de priorité ? La réponse est que la sous-classe se termine
 avec seulement un slot de ce nom, et les propriétés de ce slot sont une
 combinaison des propriétés des slots hérités. Les règles pour combiner
 chaque option sont les suivantes:

 :accessor et :reader - l'union des accessors/readers de tous les slots héréditaires;
 voir la section 4 ci-dessous pour le sens où cela fonctionne si les noms sont répétés.

 :initarg - l'union des arguments d'initialisation de tous les slots hérités.
 Par exemple, les :initargs valides pour le slot comes-from dans figurine-ardvark sont
 :comes-from et :made-in.

 :initform - la valeur initiale par défaut plus spécifique (i.e. la première :initform
 pour cet slot dans la liste de priorité). Par exemple, l':initform pour diet
 de figurine-aardvark est nil.

 :allocation - pas d'héritage; contrôle uniquement par la classe étant définie;
 par defaut :instance.

 Exemple:

 CL-USER 43 > (setf Eric (make-instance 'figurine-aardvark
                      :legs 4
                      :made-by "Jen"
                      :made-in "Brittany"
                      :aardvark-name "Eric"))
#<FIGURINE-AARDVARK>

 CL-USER 44 > (shiftf (cute-p Eric) t)

NIL

 CL-USER 45 > (slot-value Eric 'diet)

NIL

 Soyez averti qu'il est assez facile d'utiliser l'héritage de façon inappropriée;
 l'héritage multiple se multiplie tellement, s'il vous plaît prenez un peu de
 soin. Demandez-vous si foo veut vraiment hériter de bar, ou si les instances
 de foo veulent un slot contenant un bar.
 Un bon guide général est que si foo et bar sont de objets de même type alors
 il est correct de les mélanger par héritage, mais s'ils sont vraiment des
 concepts distincts, alors vous devriez utiliser des slots pour les séparer.

 Par exemple, supposons que votre application veut dessiner une image d'un
 feu de circulation. (drawable-traffic-light = feu triclor dessinable) ?
 La classe drawable-traffic-light veut probablement hériter de drawable et
 avoir un slot pointant sur chaque instance de traffic-light.
 Le mélange des classes ensemble avec ce truc flashy (clinquant) héritage
 multiple ne fera que faire des sacs de noeuds.
 Si, suivant votre code il dépend d'une compréhension de comment le tri
 topologique fonctionnne, ou de l'examen détaillé de nombreuses classes
 de comprendre pourquoi vous n'avez pas l':initform que vous vouliez,
 alors vous avez suivi un trop long chemin. Reculer.

 Cette section traite brièvement deux sujets: la redéfinition d'une classe
 existante, et de l'évolution d'une instance d'une classe dans une instance
 d'une autre. Dans les deux cas, nous allons passer sous silence les détails:
 il suffit de dire qu'ils sont tirés par les cheveux, mais tout est configurable.
 Pour redéfinir une classe, il suffit simplement d'évaluer une nouvelle forme
 de defclass. Elle prend alors la place de l'ancienne définition, la  classe
 existante est mis à jour, ainsi que toutes les instances de la classe
 (et - de manière récursive - ses sous-classes) sont mises à jour pour refléter
 la nouvelle définition. Par exemple:

 CL-USER 46 > (list Eric (class-of Eric) (slot-exists-p Eric 'has-tail-p))

(#<FIGURINE-AARDVARK #x000334596EB8> #<STANDARD-CLASS FIGURINE-AARDVARK> NIL)

 On redéfinit la classe animal:

 CL-USER 47 > (defclass animal ()
         ((legs :reader leg-count :initarg :legs)
           (has-tail-p :reader has-tail-p :initform t)
           (comes-from :reader comes-from :initarg :comes-from)))

WARNING: DEFCLASS: Class FIGURINE-AARDVARK (or one of its ancestors)
is being redefined, instances are obsolete
#<STANDARD-CLASS ANIMAL :VERSION 1>

 CL-USER 48 > (list Eric (class-of Eric) (slot-exists-p Eric 'has-tail-p))

(#<FIGURINE-AARDVARK #x000334596EB8> #<STANDARD-CLASS FIGURINE-AARDVARK :VERSION 1> T)

 Vous pouvez redéfinir les classes alors qu'une application est
 en cours d'exécution, exactement de la même manière et pour les
 mêmes raisons que vous pouvez redéfinir les fonctions.
 La grande force de redéfinition de classe est bien au cours
 du développement de l'application.
 Par exemple, vous pouvez revoir une classe et ajouter un slot ou
 une superclasse à laquelle vous n'aviez pas pensé plus tôt, sans
 avoir à recompiler rien d'autre que la nouvelle defclass, et sans
 invalider l'un de vos objets.

 Pour changer la classe d'une instance, utilisez le changement de classe:

 CL-USER 49 > (defclass antelope (mammal)
          ((diet :reader munched-by)))

#<STANDARD-CLASS ANTELOPE>

 CL-USER 50 > (change-class Eric 'antelope :diet 'greens)

#<ANTELOPE #x000334596EB8>

 CL-USER 51 > (list (slot-exists-p Eric 'potter) (munched-by Eric))

(NIL GREENS)

 Dans l'exemple ci-dessus, un aardvark céramique est devenue un gracieux
 ruminants, le slot potter est perdu automatiquement et est explicitement
 mis sur un régime alimentaire sain de légumes verts (entre autres changements).
 Laissant de côté les questions de cruauté envers les animaux, c'est une
 fonctionnalité puissante de CLOS bien que probablement, une de celles que
 vous n'utiliserez pas très souvent.

 3.8 Notes d'implémentation: les enveloppes d'objets.
 °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
 Nous allons conclure cette partie du tutoriel en regardant une implémentation
 possible pour les instances, couvrant:
	-o- l'accès aux slots locaux et de classe,
	-o- comment une instance connaît sa classe mais
	    pourquoi une classe ne connaît pas ses instances,
	-o- préservation de l'identité quand une classe est redéfinie,
	-o- mise à jour paresseuse des slots.

 Cette section est très spécifique à l'implémentation, même si j'ai un
 sentiment intime que de nombreuses implémentations auront suivi une
 voie similaire. Les exemples sont à titre indicatif seulement.

 Une enveloppe est une structure interne.
 Chaque classe - y compris les structure-classes et built-in-classes - a
 une enveloppe. La classe pointe sur l'enveloppe et l'enveloppe pointe sur
 la classe. Chaque appel à make-instance alloue deux nouvelles structures:
 un vecteur de slots d'instance et l'instance elle-même.
 La structure interne de l'instance est petite: elle a deux slots, pointant
 sur l'enveloppe et le vecteur de slots.

			------------
		--------| instance |-------------
		|	------------	        |
		|			        |
		|			        |
		v			        v
	   -----------			------------------
	   | wrapper |			| instance slots |
	   -----------			------------------
	     |     ^
	     |	   |
	     |	   |
	     |	   |
	     v	   |
	   ----------
           | class  |
	   ----------

   figure 4.  Instance, classe et enveloppe. Les flèches indiquent le sens des relations.

 Nous pouvons définir class-of comme cela:

 (defun class-of (object)
   (if (built-in-object-p object)
     (built-in-class-of object)
     ;; structures wrappers are similar enough to CLOS instances
     (wrapper-class (instance-wrapper object))))

 L'enveloppe a les slots suivants:
 (notez l'absence de pointeurs de l'enveloppe à l'instance)

 -o- class - pointe sur la classe de l'instance.
 -o- cache-nombre - un fixnum unique à la définition de la classe; chaque
     fois qu'une classe est définie (y compris les structures et de les
     built-in classes, pour des raisons qui seront clairement indiquées à
     la fin de l'article 4) une variable globale est incrémenté et sa valeur
     est utilisée pour la nouvelle enveloppe.
 -o- instance-slot-names - une suite de noms de slots locaux de l'instance.
     Cela sert à décrire la disposition des slots de l'instance.
 -o- shared-slots - une liste d'association de noms de slots partagés et
     les valeurs correspondantes.

 (Note d'implémentation: la fonction wrapper-of ci-dessous ne fait pas
 partie de Common Lisp. Dans Allegro elle est dans le package excl.)

 NDT : pas trouvé l'équivalent dans clisp ni dans SBCL avec le package
 cl-acl-compat (Compatibility layer for Allegro Common Lisp) installé ?

 CL-USER 52 > (clos::wrapper-of Eric)
#<record 1513 (LEGS HAS-TAIL-P COMES-FROM DIET) NIL
#<STANDARD-CLASS ANTELOPE 2115243C>>

 Avec SBCL:
 (sb-pcl::valid-wrapper-of Eric)
#<SB-KERNEL:LAYOUT for ANTELOPE {C762021}>

 Une implémentation de slot-value pourrait prendre les lignes suivantes, si ce
 n'était pour les problèmes d'inefficacité et - en particulier - plusieurs
 échecs de l'API CLOS que nous n'aborderons pas ici.

 (defun slot-value (instance slot-name)
  (validate-instance instance)                   ; See below
  (let* ((wrapper (instance-wrapper instance))
         (local-slot-names (wrapper-local-slot-names wrapper))
         (local-position (position slot-name local-slot-names))
         (value (if local-position
                    ;; It's a local slot.
                    (let ((local-slots (instance-slots instance)))
                      (svref local-slots local-position))
                  (let* ((shared-slots (wrapper-shared-slots wrapper))
                         (shared-slot (assoc slot-name shared-slots)))
                    (if shared-slot
                        ;; It's a class slot.
                        (cdr shared-slot)
		      ;; It's not a slot of this instance.
                      (slot-missing-error instance slot-name))))))
    (if (eq value (the-unbound-slot-value))
        ;; The slot-value has not yet been set. Always an error in CLOS.
        (slot-unbound-error instance slot-name)
      value)))

 Si une classe est redéfinie elle obtient une nouvelle enveloppe d'un nouveau
 cache-number, et qui reflète la nouvelle répartition des slots.
 Le cache-number de la vieille enveloppe est remis à zéro (cela marque le
 wrapper comme invalidé). Rien d'autre n'est fait à ce stade. CLOS n'a pas
 besoin de mettre à jour les instances jusqu'à ce qu'elles soient "touchées"
 par l'application. Une classe pourrait donc être mise à jour à plusieurs
 reprises sans qu'aucun travail ne soit fait sur ses instances.

							------------
						--------| instance |-----
						|	------------    |
						|			|
						V			V
		===============	    -----------------------	------------------
	   -----| new wrapper |<--  | invalidated wrapper |	| instance slots |
	   |	===============	 |  -----------------------	------------------
	   |			 |		|
	   V			 V		|
  ====================	    =========		|	-------------------
  | new slots layout |	    | class |<----------'------>| old slot layout |
  ====================	    =========			-------------------

  figure 5. "Invalide" instance, classe, anciens et nouveaux wrappers.
	   Les strutures encadrées avec le signe "=" ont été mises à jour.


 Chaque fois que l'application accède à une instance - par exemple dans la
 définition du slot-value ci-dessus - le système CLOS a besoin de savoir si
 cette instance a été invalidée, et ce contrôle ne doit être cher parce que
 cela va se produire souvent. Dans la pratique, validate-instance et tout
 ce qu'elle appelle, à l'exception de la fonction de revalidation, serait
 implémenté avec des macros ou des fonctions pour minimiser les coûts en
 ressource.

 (defun validate-instance (instance)
   (let ((wrapper (instance-wrapper instance)))
     (when (zerop (wrapper-cache-number wrapper))
       ;; Instance needs revalidation
       (revalidate-instance instance))))

 La revalidation est un processus laborieux, mais le plan général est assez
 clair: vous suivez la chaîne de pointeurs grâce à slot-layout, vous le
 comparez avec l'ancien layout, et construisez un nouveau vecteur de slots
 en utilisant la nouvelle description et autant de la les anciennes valeurs
 que sont toujours valables. L'instance est laissé pointant vers son vecteur
 de slots rafraichit et le nouveau wrapper de classe.

 Notes finales:

  -o- change-class suit un chemin similaire à la revalidation décrite
      ci-dessus, le réglage de la nouvelle enveloppe de classe et un
      vecteur de slot rafraichit dans une instance existante;
  -o- class-of ne nécessite pas de validation: l'ancienne classe a été
      modifiée plutôt que remplacée et ainsi l'ancien wrapper pointe sur
      la classe (mise à jour);

  -o- l'indirection des wrappers (voir la figure 4 ci-dessus) permet
      recherche dynamique des noms de slots, redéfinition dynamique via
      eq, et la modification lente, le tout à un faible coût en ressource.

4. Methods
==========

4.1. Rappel - l'approche non-OO
°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
 L'étape allant des fonctions à des méthodes est parallèle au passage
 de structures aux instances.
 Suposons que l'on veuille mettre en place notre propre - simplifiée -
 version de describe.

 CL-USER 53 > (defun my-describe (thing)
        (typecase thing
         (cons   (describe-cons thing))
         (symbol (describe-symbol thing))
         (array  (describe-array thing))
         (number (describe-number thing))
         ;; [ etc etc etc ]
         (t      (describe-whatever thing))))

 [54]> (defun describe-symbol (symbol)
         (let ((package (symbol-package symbol))
	     (boundp (boundp symbol)))
          (format t
       	         "~s is a symbol. ~
	         It ~:[~*does not have a home~;is in the ~s~] package. ~
		    	   Its value is ~:[unbound~;~s~]."
				symbol
				package (when package (package-name package))
				boundp (when boundp (symbol-value symbol)))))

DESCRIBE-SYMBOL

 CL-USER 55 > (my-describe :foo)

:FOO is a symbol. It is in the "KEYWORD" package. Its value is :FOO.

 CL-USER 56 > (my-describe '#:foo)

#:FOO is a symbol. It does not have a home package. Its value is unbound.

 Il ya un certain nombre de problèmes avec cela:

 -o- Il n'y a aucune obligation que typecase soit efficace. Bien sûr, dans le cas
     de my-describe ce n'est pas grave, parce que le code ne s'exécute qu'une
     seule fois par interaction de l'utilisateur et ainsi personne ne pourra
     jamais remarquer s'il n'est pas aussi rapide qu'il aurait pu l'être.
     Mais la discrimination en fonction du type d'une valeur de programme est
     une opération courante en Lisp et il y a de nombreuses occasions (par exemple
     l'implémentation d'une interface graphique), où le nombre de possibilités
     peut être non-trivial et les coûts en ressource dans le parcours d'une
     liste de cas sont inacceptables.

 -o- Nous devons prendre soin d'ordonner les déclarations
     (le cas pour nulle devra précéder symbol.)

 -o- Supposons que nous voulions faire de la discrimination en fonction des
     types de plus d'une valeur ?

      (typecase (cons thing stream)
        ((cons array non-scrollable-io)
         (describe-array-non-scrollable array stream))
        ((cons array scrollable-io)
         (describe-array-scrollable array stream))
        ((cons array output-stream)
         (describe-array-general-stream array stream))
        ...)

  -o- Comme nous continuons à penser à des cas, la définition de my-describe
      est de plus en plus longue, quand nous continuons à la revisiter pour
      ajouter d'autres clauses.

 -o- Les fonctions subsidiaires risquent également d'être de plus en plus
     longues. (Supposons que nous distinguons sur trois valeurs, ou quatre)
     Le code devient rapidement moins lisible.

 4.2 Introduisons la macro defmethod
 °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
 La macro définie pour contrôler la discrimination de type dans CLOS est
 defmethod. Un exemple:

 CL-USER 57 > (fmakunbound 'my-describe)

MY-DESCRIBE

 CL-USER 58 > (defmethod my-describe (thing)
    (format t "~s could be anything, for all I care." thing))

#<STANDARD-METHOD (#<BUILT-IN-CLASS T>)>

 CL-USER 59 >  (defmethod my-describe ((animal animal))
       (format t
     	    "~s is an animal. It has ~d leg~:p ~and comes from ~a."
   	    animal
 	    (leg-count animal)
	    (comes-from animal)))

#<STANDARD-METHOD (#<STANDARD-CLASS ANIMAL :VERSION 1>)>

 CL-USER 60 > (my-describe Eric)

#<ANTELOPE #x000334596EB8> is an animal. It has 4 legs and comes from Brittany.
NIL

 CL-USER 61 > (my-describe (make-instance 'figurine))

#<FIGURINE #x0003345ECDF0> could be anything, for all I care.
NIL

 La forme de defmethod ressemble - et est similaire à - defun. Elle associe
 un ensemble de code avec le nom de la fonction my-describe, mais
 - contrairement à une fonction ordinaire - ce corps ne peut être exécuté que
 si les types des arguments correspondent au modèle déclarée par la liste lambda.

 Notez que la syntaxe de l'invocation d'une méthode est exactement la même
 que la syntaxe pour invoquer une fonction ordinaire. Vous ne pouvez pas
 dire à partir du code d'appel (par exemple les lignes 60 et 61 ci-dessus)
 si l'appel est à une fonction ordinaire ou une méthode CLOS.
 Vous pouvez appeler des méthodes par des fonctions ordinaires, et des
 fonctions ordinaires par des méthodes, et généralement les mélanger ensemble.

 Passons à la forme defmethod elle-même, la façon dont ce modèle fonctionne
 est que les paramètres requis dans la lambda liste de la méthode peuvent
 prendre l'une des deux formes suivantes: variable ou (variable specializer).
 Dans le premier cas, la variable est liée à la valeur de l'argument correspondant
 comme d'habitude. Tandis que, dans le dernier cas, la variable est liée à
 l'argument correspondant seulement si cet argument est de la classe specializer
 (ou d'une sous-classe). Si aucun argument ne correspond au specialiser alors
 la méthode n'est pas applicable et ne peut pas être exécutée avec ces arguments.

 Vous pouvez définir un nombre quelconque de méthodes avec le même nom de
 fonction, mais avec différents specializers. Le système choisit la
 méthode la plus spécifique - qui est, la méthode applicable dont les
 specializers sont les plus proches à la tête de la class-precedence-list
 correspondant à chaque argument - et exécute son corps.

 Dans l'exemple ci-dessus, nous avons défini deux méthodes sur my-describe.
 La première n'est pas spécialisée sur son argument et donc la méthode est
 toujours applicable. La deuxième méthode spécialise son argument sur la
 classe animal, et n'est donc applicable que si cet argument est un animal.

 Dans la ligne 60, nous décrivons un animal. Les deux méthodes sont applicables.
 Comment le système choisira laquelle invoquer ?

 CL-USER 62 > (mapcar 'class-name (sb-mop:class-precedence-list (class-of Eric)))

(ANTELOPE MAMMAL ANIMAL STANDARD-OBJECT T)

 La méthode spécialisée est plus spécifique parce que son specializer
 apparaît plus tôt dans la liste de précédence que la methode non qualifiée
 (ou par défaut): animal précède t. Une autre façon d'exprimer ceci est
 que la méthode spécialisée supplantent celles non spécialisées.

 En ligne 61, nous décrivons une figurine. Cette fois, seule une des
 deux méthodes est applicable, parce que la classe figurine n'est pas
 une sous-classe de la classe animal.

 Ce processus d'appariement a deux conséquences:

 -o- Envoi par la discrimination en fonction du type d'une valeur de
     programme, ce qui est exactement ce que nous cherchions.
 -o- Comme un effet de bord intéressant, une garantie implicite sur
     la classe d'une valeur spécialisée à l'intérieur du corps de la
     méthode, qui a des implications pour l'optimisation.
     (par exemple, des appels à la slot-value)

 notes:
 -o- C'est une erreur de définir une méthode avec le même nom qu'une
     fonction ordinaire, d'où l'appel à fmakunbound ci-dessus.
 -o- Les méthodes peuvent être redéfinis exactement comme pour les
     fonctions ordinaires.
 -o- L'ordre dans lequel les méthodes sont définies est sans importance,
     bien que toutes les classes sur lequelles elles se spécialisent
     doivent déjà exister.
 -o- Un argument non spécialisé est plus ou moins équivalent à un argument
     étant spécialisé sur la classe t. La seule différence est que tous les
     arguments spécialisés sont implicitement pris pour être "referred to",
     (dans le sens de declare ignore).
 -o- Chaque forme defmethod génère (et retourne) une instance CLOS,
     de la classe standard-method.

 Exercise: All CLOS objects are printed by a method on print-object, whose
 arguments are (object stream). Define methods for printing aardvarks and
 antelopes more interestingly than by the default method. How might the
 default method (for printing a standard-object) be defined?

 Exercise (in which I am indebted to Steve Haflich for his clarifications):
 Consider the following code and form unassailable opinions as to the
 circumstances in which a compiler might be entitled to eliminate either
 of the tests in the method body.

 (defclass frob (standard-object) ())

 (defmethod foo ((baz frob))
    (loop initially (mangle)
       while baz do
          (etypecase baz
             (frob (setf baz (bar baz)))))))

4.3. Fonctions génériques et méthodes associées
°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
 Une fonction générique est une fonction Lisp qui est associé à un ensemble
 de méthodes et les distribue quand elle est invoquée. Toutes les méthodes
 qui ont le même nom de fonction appartiennent à la même fonction générique.
 La première fois que nous avons défini une méthode my-describe, nous avons
 créé implicitement une fonction générique avec ce nom. La fonction générique
 avait initialement une seule méthode, jusqu'à ce que nous ayions ajouté une
 deuxième méthode avec le même nom.
 (Note d'implémentation: les fonctions  generic-function-methods et
 method-generic-function  ci-dessous ne font pas partie de Common Lisp. Dans
 LispWorks ils sont disponibles via package-use-list par défaut, dans Allegro
 ils sont exportés de ACLMOP..)

 NDT: dans clisp elles sont implémentées

 CL-USER 63 > #'my-describe

#<STANDARD-GENERIC-FUNCTION MY-DESCRIBE>

 CL-USER 64 > (generic-function-methods #'my-describe)

(#<STANDARD-METHOD (#<STANDARD-CLASS ANIMAL :VERSION 1>)>
#<STANDARD-METHOD (#<BUILT-IN-CLASS T>)>)

 CL-USER 65 > (method-generic-function (car *))

#<STANDARD-GENERIC-FUNCTION MY-DESCRIBE>

 Quelques remarques:
 -o- Dans la section 4.2 ci-dessus nous avons parlé de "l'invocation d'une méthode".
     Pour être précis, l'application ne peut pas invoquer directement une méthode.
     Si l'application appelle une fonction qui se trouve être une fonction générique,
     alors celle-ci  enverra (i.e. invoke) la méthode la plus applicable.
 -o- Les méthodes peuvent avoir des arguments optional, keyword et &rest. Ceux-ci
     doivent être compatibles (lambda listes congruentes) entre chaque méthode de chaque
     fonction générique. Par exemple, s'il existait un argument optional stream dans
     l'une des deux méthodes dans my-describe alors cet argument devra être présent et
     optional dans l'autre.
 -o- Tous les accessors/readers définis par defclass sont des méthodes. Elles peuvent
     remplacer ou être remplacées par d'autres méthodes sur la même fonction générique.

 Lorsqu'une fonction générique est appelée, le mécanisme de répartition se déroule comme suit:

 -1- Calcul la liste des méthodes applicables;
 -2- S'il n'y-a pas de méthode applicable alors signaler une erreur;
 -3- Trier les méthodes applicables dans l'ordre de spécificité;
 -4- Invoquer la méthode la plus spécifique.

 Lors de l'exécution d'une méthode, les méthodes applicables restantes sont toujours
 accessibles, via la fonction locale call-next-method.
 Cette fonction a une portée lexicale à l'intérieur du corps de la méthode, mais
 une étendue indéterminée. Elle invoque la prochaine méthode la plus spécifique, et
 retourne toute valeur que cette méthode retourne. Elle peut être appelée avec soit:

 -o- avec aucun argument, auquel cas la méthode suivante recevra exactement les mêmes
     arguments que cette méthode; ou
 -o- avec des arguments explicites, auquel cas il est nécessaire que l'ensemble trié
     des méthodes applicables aux nouveaux arguments soit le même que celui calculé
     lorsque la fonction générique a été appelée pour la première fois.

 L'appel de call-next-method  quand il n'existe aucune méthode suivante signale
 une erreur. Vous pouvez savoir si une méthode suivante existe en appelant la fonction
 locale next-method-p (qui a aussi une portée lexicale et l'étendue indéfinie).

  CL-USER 66 > (defmethod my-describe ((antelope antelope))
               (if (string= (slot-value antelope 'comes-from)
                            "Brittany")
                   (format t "Eric? Is that you?")
                 (call-next-method)))

WARNING: On change la fonction générique #<STANDARD-GENERIC-FUNCTION MY-DESCRIBE>
qui a déjà été appelée.
#<STANDARD-METHOD (#<STANDARD-CLASS ANTELOPE>)>

 CL-USER 66 > (my-describe
              (make-instance 'antelope :comes-from 'nowhere :legs 4))

#<ANTELOPE #x00033459B318> is an animal. It has 4 legs and comes from NOWHERE.
NIL

 CL-USER 68 > (my-describe Eric)

Eric? Is that you?
NIL

 Notez enfin que le corps de chaque méthode établit un bloc avec le même nom
 que la fonction générique de la méthode. Si vous "return-from" ce nom vous
 quittez la méthode actuelle, pas l'appel à la fonction générique englobante.

 Exercice: Utilisez votre implémentation Lisp, pour jeter un oeil à la
 class-precedence-list des fonctions génériques.

 Exercice: Lorsque vous évaluez (comes-from Eric), de quelle la classe le
 reader hérite t-il ? Remplacer cette méthode, de sorte que les antelopes
 proviennent toujours de l'Afrique. (Ce ne est pas vrai, mais ce est une amélioration.)

 Exercice: Expérimentez avec l'étendue indéfinie de call-next-method.

 4.4. Dans les langages OO la fonctionnalité vit dans l'objet
 °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
 Cette affirmation absurde est le produit d'une imagination malade. Cependant,
 de nombreux systèmes OO se sentent obligés d'essayer de l'appliquer.
 Essayez d'éviter d'avoir à programmer dans un de ces systèmes.

 En ce qui concerne CLOS, la vérité est que - à l'exception des slots accessors -
 toutes les fonctionnalités de votre application vivent en définition de
 fonctions et de méthodes, pas dans les classes.

 Il est parfois approprié de placer les méthodes applicables à une classe
 dans le même fichier que cette classe. Il est parfois approprié de placer
 toutes les méthodes d'une fonction générique en un seul fichier. Il n'y a
 pas de contraintes du langage à ce sujet.

 En accord avec le titre de cette section vient la notion de passage de messages.
 Cela découle des systèmes OO qui vous permettent uniquement de spécialiser sur
 le premier argument. Cet argument a alors une proéminence linguistique, et
 l'appel de fonction a une nouvelle syntaxe pour indiquer cela : Eric<-(my-describe)

 Vous devez interprèter cela comme envoyer le message (my-describe) à Eric.
 (dans ce cas sans arguments supplémentaires). C++ est un coupable évident:
 Eric::my_describe();

traduction à revoir

 CLOS supporte le multi-methods - méthodes qui peuvent se spécialiser sur
 plus d'un argument. Bien que vous ne pourriez pas l'utiliser tant que cela,
 c'est libérateur. Non seulement il vous libère de code vraiment horrible
 mais vous pouvez vous retrouver avec des programmeurs qui luttent pour
 contourner les restrictions du paradigme du message-passing, mais il
 implique que les méthodes n'ont pas à vivre à l'intérieur des classes.
 (Si une méthode est spécialisée sur deux classes, dans laquelle devra
 t-elle être ?) Une conséquence est que vous pouvez redéfinir l'une de vos
 méthodes sans avoir à recompiler la classe et avec elle 500 autres méthodes.
 C'est bien.

 Une note de style: il ya une tendance occasionnelle, peut-être empruntée
 à des langages qui ne supportent pas le multi-methods, est d'appeler
 l'argument spécialisé self:

 (defmethod wibble ((self aardvark) ...) ...)

 si les méthodes d'une fonction générique spécialisent uniquement sur le
 même argument, ce ne est pas mieux ou pire que d'appeler les arguments
 après la classe, qu'ils spécialisent:

 (defmethod wibble ((aardvark aardvark) ...) ...)

 Faites tout ce qui peut rendre votre code plus clair.

 Exercice: Le describe de lisp est implémenté par la fonction générique
           describe-object.
	   Les implémentations sont invités à définir des méthodes suffisantes
  	   (et les utilisateurs sont encouragés à en ajouter plus s'ils aiment),
	   spécialisées sur les deux arguments object et stream. Discutez pour
	   savoir si les développeurs devraient grouper toutes les méthodes sur
	   describe-objet en un seul fichier, ou les répartir dans plusieurs
	   (de telle sorte que, par exemple une méthode spécialisée sur un
	   aardvarks devraient être dans le fichier "aardvark.lisp" avec la
	   définition de classe et d'autres méthodes ). Quels sont les problèmes ?

 Exercice: Trouver une excuse pour spécialiser une méthode sur son deuxième argument,
 	   ou sur plus d'un argument.

 4.5. Autres specializers (de nouveau vous n'avez pas besoin des objets CLOS pour utiliser CLOS)
 °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
 Les exemples de méthodes indiquées plus haut sont toutes spécialisées
 sur standard-class Ce n'est pas nécessaire. Vous pouvez spécialiser
 sur toute classe CLOS: par exemple, les classes de systèmes énumérés
 près du sommet de la section 3.4, ou de toute classe de structure.

 CL-USER 69 > (defmethod my-describe ((self structure-object))
               (format t "~s is a structure object."
                       self))

 WARNING: On change la fonction générique #<STANDARD-GENERIC-FUNCTION MY-DESCRIBE>
 qui a déjà été appelée.

#<STANDARD-METHOD (#<STRUCTURE-CLASS STRUCTURE-OBJECT>)>

 CL-USER 70 > (my-describe (make-foo))

#S(FOO) is a structure object.
NIL

 CL-USER 71 > (defmethod my-describe ((self foo))
               (format t "bar"))

 WARNING: On change la fonction générique #<STANDARD-GENERIC-FUNCTION MY-DESCRIBE>
 qui a déjà été appelée.

#<STANDARD-METHOD (#<STRUCTURE-CLASS FOO>)>

 CL-USER 72 > (my-describe (make-foo))
bar
NIL

 Vous pouvez utiliser les méthodes dans votre code sans jamais définir une
 classe CLOS, tout comme vous pouvez utiliser des classes CLOS sans un seul
 defmethod. Ces deux parties de CLOS sont indépendantes. Considérez-les
 comme deux systèmes d'objets pour le prix d'un.

 Une autre forme de specializer, qui parfois peut se révéler utile, est
 connue comme un specializer eql. Dans ce cas, le nom de classe spécialisée
 est remplacé par une liste dont le premier élément est le symbole eql et
 dont la seconde valeur est n'importe quelle forme Lisp.

 Cette forme est évaluée en même temps que le defmethod. Pour que la méthode
 soit applicable, l'argument correspondant doit être eql au résultat de cette
 évaluation. Une eql method est plus spécifique qu'une qui est spécialisée sur
 les classes.

 CL-USER 73 > (defmethod my-describe ((self (eql pi)))
               (format t "approximately 22/7"))

 WARNING: On change la fonction générique #<STANDARD-GENERIC-FUNCTION MY-DESCRIBE>
 qui a déjà été appelée.

#<STANDARD-METHOD ((EQL 3.1415926535897932385L0))>

 CL-USER 74 > (defmethod my-describe ((self float))
                (format t "some float"))

#<STANDARD-METHOD (#<BUILT-IN-CLASS FLOAT>)>

 CL-USER 75 > (my-describe pi)

approximately 22/7
NIL

 Exercice: Ecrire une méthode  my-describe pour les listes.

 Exercice: Ecrire une méthode sur print-objet pour Eric l'antelope.
 	   Changez la class-of-Eric. Vous attendez-vous à ce que
 	   votre méthode soit toujours applicable ?

 4.6 Qualifiers et combinaison de methods
 °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
 Commençons par un mot d'avertissement. L'utilisation imprudente de
 la combinaison de methods peut - comme une main libre avec l'héritage
 multiple - emmêler votre code de façon spectaculaire.

 La syntaxe complète de defmethod est:

 defmethod function-name {method-qualifier}* specialized-lambda-list
          [[declaration* | documentation]] form*

 Nous allons seulement regarder ici une combinaison de méthode standard
 par défaut. (des autres combinaisons de méthodes sont disponibles, et
 vous pouvez même définir les votres propres mais je ne me souvient pas
 d'avoir rencontré quelqu'un qui l'a fait.) Avec la combinaison de méthode
 standard, pas plus d'un qualificateur de méthode n'est autorisé par
 méthode, et s'il est présent il doit être l'un des mots clés suivants:
 :before , :after et :around.

 Les méthodes sans qualification sont connus comme méthodes primaires.

 Le mécanisme de répartition complet des fonctions génériques est le
 suivant; noter que les méthodes :before et :after ne sont exécutées
 que pour leurs effets de bord.

 -1- calculer les méthodes applicables, et les répartir en listes
     séparées en fonction de leur qualification;
 -2- s'il n'y a pas de méthode primaire applicable alors signaler une erreur;
 -3- trier chacune des listes par ordre de spécificité;
 -4- exécuter la plus spécifique méthode :around et retourner tout ce qu'elle
     retourne;
 -5- si une méthode :about appelle call-next-method, exécuter la plus spécifique
     méthode suivante :about;
 -6- s'il n'y avait pas de méthodes :about, en première place, ou si une
     autre méthode :about appelle call-next-method, mais qu'il n'y a pas
     d'autre méthode :about à appeler, alors procédez comme suit:

	-a- exécuter toutes les méthodes :before, dans l'ordre, en ignorant
	    toutes les valeurs de retour et ne pas permettre les appels à
	    call-next-method ou à next-method-p;
	-b- exécuter la méthode primaire la plus spécifique et retourner tout
            ce qu'elle retourne;
	-c- si une méthode primaire appelle call-next-method, exécuter la
	    prochaine  méthode primaire la plus spécifique;
	-d- si une méthode primaire invoque call-next-method, mais qu'il
	    n'y a pas d'autres méthodes primaires à appeler ensuite signaler
            une erreur;
	-e- après que la méthode(s) primaire a terminé, exécuter toutes les
	    méthodes :after, dans l'ordre inverse, en ignorant les valeurs
	    de retour et ne pas permettre des appels à call-next-method ou
	    à next-method-p.

 Si vous pensez que tout cela semble incroyablement compliqué, vous avez
 probablement raison. Pensez-y comme un oignon, avec toutes les méthodes
 :aroud de la couche la plus externe, les méthodes :before et :after dans
 la couche intermédiaire, et les méthodes primaires à l'intérieur.
 Sachons gré qu'il 'y a que seulement trois couches. Pour faire fonctionner
 le modèle, cela aide conceptuellement de coupler les méthodes :before et
 :after comme ceci:

  (defmethod spong :before-and-after (&rest args)
    (let ((before (find-method #'spong '(:before) args))
          (after  (find-method #'spong '(:after) args)))
      (when before (invoke-method before args))
      (multiple-value-prog1
          (call-next-before-and-after-method)
        (when after (invoke-method after args)))))

 Notez comment cela nous donne un ordre inversé pour les méthodes :after.
 Cela arrive tout naturellement, ce qui pourrait expliquer pourquoi
 l'inversion a été spécifiée en première place.

 Dans la vraie vie (on espère) la situation ne sera pas si compliquée que
 cela. Un exemple simple: my-describe avec suppression des valeurs de retour.

 CL-USER 76 > (defmethod my-describe :around (self)
                 (call-next-method)
                 (values))

#<STANDARD-METHOD :AROUND (#<BUILT-IN-CLASS T>)>

 CL-USER 77 > (my-describe Eric)
Eric? Is that you?

 Un autre exemple: L'implémentation de make-instance dans CLOS se fait en deux
 étapes: allouer le nouvel objet, puis le passer avec tous les arguments mot-clé
 de make-instance, à la fonction générique initialize-instance. Les implémenteurs
 et les applications writers définissent: les méthodes :after sur initialize-instance,
 pour initialiser les slots de l'instance. Le système qui fournit une méthode primaire
 le fait en ce qui concerne (a) :initform et :initarg valeurs fournies avec la classe
 qui a été définie et (b) les mots-clés passés par make-instance. D'autres méthodes
 peuvent étendre ce comportement comme ils l'entendent. Par exemple, ils pourraient
 accepter un mot clé supplémentaire qui invoque un accès de base de données pour
 remplir certains slots. La liste lambda pour initialize-instance est:

 initialize-instance instance &rest initargs &key &allow-other-keys

 Exercice: Ajouter une méthode :after à initialize-instance pour faire
 provenir tous les aardvarks de Cambridge, en Angleterre. Ajouter une
 autre méthode (comment la qualifier ?) pour interdire l'interaction suivante:

  (make-instance 'cannibal :diet (make-instance 'cannibal))

 Exercice: Vous pouvez choisir de considérer initialize-instance comme
 analogue boosté des constructeurs offerts par d'autres systèmes OO.
 Mais CLOS n'offre pas un destructeur. Est-ce que cela importe ?

4.7. Notes d'implémentation: fonction générique dispatch
°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
 Le calcul, le tri et l'exécution d'une liste de méthodes applicables
 sont des opérations lentes. Une implémentation voudra généralement
 mettre en cache ses résultats et les réutiliser aussi souvent
 - mais le plus économiquement - que possible. Un implémenteur m'a dit
 une fois que son objectif était de faire "dans le meilleur des cas"
 qu'un appel de fonction générique dispatch ne doit pas être plus de
 trois fois moins rapide que d'appeler une fonction ordinaire.

 Dans cette méthode le cache ne peut pas être calculé à l'avance, parce que
 (a) nous ne savons généralement pas quels arguments pourraient être transmis
 à la fonction générique dans l'avenir et (b) il y a tant de classes dans le
 système que le cache pourrait être énorme, en particulier si la fonction
 générique était spécialisée sur plus d'un argument. Par conséquent,le cache
 doit être augmenté à la volée pendant que l'application s'exécute.

 Les stratégies suivantes pourraient être utiles:
 -o- Une fonction générique est à la fois une fonction et une instance de CLOS.
     Implémenter la fonction en fermant le code du modèle du système sur l'instance.
 -o- Implémenter la méthode d'exécution (étapes 4 à 6 dans le "mécanisme de
     répartition complet" décrit dans la section 4.6 ci-dessus) en fermant des
     modèles pré-compilés sur les listes triées de méthodes applicables, générant
     ainsi des méthodes combinées. Une méthode combinée est un objet funcallable
     qui prend les mêmes arguments que la fonction générique, et qui s'occupe de
     tous les aspects de la combinaison des méthodes lorsque les arguments
     appartiennent à des classes données.
 -o- Seulement appeler le compilateur - i.e. seulement générer le code dispatch
     nouveau - si aucun modèle pré-compilé n'existe. Par exemple, l'implémentation
     pourrait être préparée pour les fonctions génériques qui se spécialisent sur
     un maximum de dix arguments, et puis certains utilisateurs bienveillants
     écrirons une fonction générique qui se spécialise sur tous les onze.
 -o- Maintenir un "slow-lookup" cache au sein de chaque fonction générique, associant
     les classes d'arguments avec les méthodes combinées. Ce cache est agrandi
     chaque fois que la fonction générique est appelée avec des arguments dont les
     classes n'ont pas déjà été vu par la fonction générique.
 -o- Également maintenir des caches "fast-lookup" optimisés, associant les classes
     des arguments utilisés dans les quelques derniers appels à cette fonction
     générique avec les méthodes combinées.
 -o- Si une nouvelle méthode est ajoutée, jeter les caches plutôt que de tenter de
     les modifier. Définir une méthode est beaucoup plus rare que faire appel à une
     méthode.
 -o- Envisager des cas particuliers. Par exemple, une proportion heureusement élevée
     de fonctions génériques dans une application typique n'aura qu'une seule méthode,
     et celles-ci peuvent être optimisée en conséquence.

 L'hypothèse derrière le "fast-lookup" cache est que tout appel donné à une fonction
 générique est susceptible d'avoir des arguments de la même classe que l'un des
 derniers appels. Ce cache est responsable des "pas plus de trois fois plus lent"
 bien mentionnés ci-dessus, et donc se doit d'être très rapide effectivement.
 Une éventuelle implémentation est un vecteur plat, en utilisant le schéma de
 recherche suivant. Notez que ce schéma n'est pas alloué.

 -1- Obtenir l'enveloppe du premier argument spécialisé (rappel: les enveloppes sont
     associées à tous les objets Lisp, pas seulement aux instances CLOS). Pour CLOS ou
     les structures objets c'est très rapide, pour les objets intégrés cela vaut 
     la peine d'optimiser.
 -2- Valider l'enveloppe (de sorte que le cache-number soit mis à jour).
 -3- récupérer le cache-number de l'enveloppe, le diviser par la longueur du vecteur de
     la mémoire cache, et de prendre le reste.
 -4- Utiliser cette valeur comme index dans le cache. Si la valeur dans le cache à cet
     endroit est l'enveloppe en question, nous regardons l'emplacement suivant et les
     comparons avec l'enveloppe de l'argument spécialisé suivant, et ainsi de suite
     jusqu'à ce que nous ayons retrouvé tous les arguments avec les enveloppes dans le
     cache. Une fois arrivés là, nous regardons un endroit plus loin encore et nous
     espérons trouver la méthode combinée.
 -5- Si tout ce qui précède a échoué, nous avons un défaut de cache. Aller à la liste
     des classes et des méthodes combinées dans le cache principal et utilisez assoc
     pour localiser la méthode combinée. (Si cela échoue aussi, calculez la méthode
     combinée - à ce stade, nous devons serrer les dents et allouer - et l'ajouter à
     la mémoire cache lente) Ecrire les arguments des enveloppes et les méthode combinée
     dans le cache rapide.

 Pour discuter au cours du dîner: Que devrait être la taille du cache fast-lookup?

 Pour discuter au cours du dîner: Comment une implémentation pourrait optimiser
 le processus d'obtention des enveloppes des objets intégrés (tels que les
 nombres et les chaînes)?  Les réponses impliquant assoc sur le type ou
 typecase, sont incorrectes.

 Pour discuter au cours du dîner: Comment ce qui précède pourrait être modifié pour tenir
 compte des méthodes eql?


 A. Références
 =============
 [Graham 1995] 	"ANSI Common Lisp"; Paul Graham;
 Prentice Hall; 1995; ISBN 0133708756.
 See http://www.paulgraham.com/acl.html
 [Keene 1989] 	"Object-Oriented Programming in Common Lisp"; Sonya E. Keene;
  Addison-Wesley; 1989; ISBN 0201175894.
 [Kiczales et al 1991] "The Art of the Metaobject Protocol";
  Gregor Kiczales, Jim des Rivières, Daniel G. Bobrow;
 MIT Press; 1991; ISBN 0262610744.
 [Pitman 1996] "The Common Lisp Hyperspec";
 Kent M. Pitman (editor); 1996.
 Available online at http://www.lispworks.com/reference/HyperSpec/Front/index.htm
 [Steele 1990] "Common Lisp the Language, 2nd edition";
 Guy L. Steele Jr.; Digital Press; 1990; ISBN 1555580416.
 Available online at http://www-2.cs.cmu.edu/Groups/AI/html/cltl/cltl2.html

 B. Document History
 ===================
 2003-07-15	<ndl@ravenbrook.com>	Placeholder document created.
 2003-08-13	<ndl@ravenbrook.com> 	Drafting started.
 2003-08-26	<ndl@ravenbrook.com> 	First draft complete.
 2003-09-01	<ndl@ravenbrook.com>	Corrections following review.

 Le dispatch multiple est une fonctionnalité de certains langages orientés
 objet ou langages fonctionnels dans lesquels une fonction ou une méthode
 peut être spécialisée pour plus d'un de ses paramètres formels.
 On l'appelle alors multiméthode.

 C. Hiérarchie de classe partielle
 =================================

					-----
			----------------| t |---------------------
			|		-----			 |
			|					 |
			|					 |
			|					 |
			|					 V
			|				 -------------------
			|				 | standard-object |---------------------
			|   				 -------------------			|
			|					 |				|
			|					 |				|
			|					 |				|
			V					 V				V
		  ------------			  	   --------------		   ----------
		  | function |			   	   | metaobject |		   | animal |
		  ------------			   	   --------------		   ----------
			|					|   |				|
			|					|   |				|
			|					|   |				|
			|	  --------------------		|   |				|
			--------->| generic-function |<----------   |				V
				  -------------------- 		    |			   ------------
								    |			   | antelope |
								    V			   ------------
								---------
							--------| class |--------
							|	---------	|
							|			|
							V			V
						------------------	------------------
						| standard-class |	| built-in-class |
						------------------	------------------
							|
							|
							V
					 ------------------------------
					 | funcallable-standard-class |
					 ------------------------------

 Figure 6. Hiérarchie de classe partielle. Les flèches indiquent la relation "X est superclasse de Y".

 Toutes les classes sont des instances de standard-classes, à l'exception
 de t et des fonctions qui sont des instances de built-in-class et
 generic-fonction qui est une instance de funcallable-standard-class.

=============================================================================
 This document is provided "as is", without any express or implied warranty.
 In no event will the author be held liable for any damages arising from the
 use of this document. You may make and distribute verbatim copies of this
 document provided that you do not charge a fee for this document or for its
 distribution.
=============================================================================