Un des aspects le plus difficiles à saisir
de la sémantique des langages objets concerne le
caractère polymorphe de certaines constructions.
Dans la mesure où le polymorphisme en général englobe
plusieurs notions très différentes et parfois difficiles à faire
co-exister,
cet aspect des langages objets reste terriblement obscur
dans une grande majorité d'ouvrages qui leurs sont dediés.
Par ailleurs c'est dans la sosphistication des constructions
polymorphes que les objets d'Ocaml montrent tous leurs avantages.
Nous commencons donc cette partie du cours par un description intuitive
du polymorphisme dans les langages objets.
Considérons l'exemple suivant d'héritage entre classes:
class point init = object val mutable x = init method getx = x method move d = x<-x+d end;; class colorpoint x (cinit : string) = object inherit point x val mutable c = cinit method getcolor = c method setcolor c' = c<-c' end;;
let origin (p : #point) = p#getx = 0;;
La notation (p :#point)
est lue
p:Type_Instance_de(point)
Considérons le code
let monpoint = new point 0; let monCP = new colorpoint 0 "rouge";; origin(monpoint);; origin(monCP);;La fonction origin attend un objet instance de point (par exemple, monpoint). Peut-on appliquer cette fonction aux objets de type colorpoint? On peut considérer l'appel origin(monCP) comment une tentative de re-utiliser la fonction avec un type différent de celui pour lequel elle a été intialement conçue. Dans un langage classique, cet appel provoque une erreur de typage car les types Type_Instance_de(point) et Type_Instance_de(colorpoint) sont considérés incompatibles car différents. Dans les langages objet, cet appel est valide grâce à une interprétation plus fexible du typage, connue sous le nom de polymorphisme.
Polymorphisme: Une fonction, une méthode ou un objet est
polymorphe si on peut l'employer avec plusieurs types.
Par exemple, une fonction qui donne la longueur d'un tableau est
polymorphe en Ocaml: elle peut-être employeée sur n'importe quel
type de tableau. Les fonctions ou méthodes qui ont un seul type
sont dites monomorphes.
Avant d'aller
plus avant dans l'étude de cette notion, voyons qu'elle est problème
posé ici par une interprétation ``plus flexible du typage''.
Le typage a en charge la vérification de cohérence de types
d'un programme dans le but de diminuer les erreurs. Lorsqu'il est
est statique, le typage
doit garantir qu'un programme
correctement typé ne
produira pas d'erreurs dus au typage pendant l'exécution.
Cela est communément appelé sécurité du typage, et est
une propriété indispensable du typage statique.
Elle impose que toute flexibilité dans
l'interprétation du typage, c.a.d., tout polymorphisme, doit
préserver la sécurité du typage. Donc, si on veut
utiliser une valeur ou fonction avec plusieurs types, cela
doit se faire de manière sûre, sans provoquer
d'erreurs dus au typage pendant l'exécution. Considérons une fonction
f quelconque, définie par:
let f (o : t) = ...code avec opérations sûr o ...
f est polymorphe si, par la suite, le code suivant est
bien typé (et donc, ne produit pas d'erreur à l'exécution)
f(a) où a : t'
et
.
Quelles conditions doit remplir le type t' pour que cet usage soit sûr? Les informations dont on dispose sont
o#m
, alors m est nécessairement une
méthode de la classe t.
Dans les langages objets (mais aussi dans d'autres paradigmes de programmation), on trouve trois possibilités pour expliquer la forme qui prendront t et t' afin de permettre cet usage polymorphe avec les impératifs de sécurité du typage.
let origin (p) = (p:>point)#getx = 0;;La notation (p:>point) est une contrainte sur le type de p. Elle est lue le type de doit être un sous-type de point. Un sous-type t' de point est un type d'objet tel que
# let pc = new point_colore 4 "blue";; # origin (pc);; - : bool = falseExaminons la deuxième condition. Si la méthode qui est envoyé vers l'objet de t' possède un type compatible avec celui dans point, il n'y aura pas d'incompatibilité de typage par rapport au comportément attendu par le code de origin. En effet, cette fonction attend que le type résultat de getx soit int, autrement, il est impossible de réaliser la comparaison ...#getx = 0. La méthode getx a le type int aussi bien dans point que dans colorpoint. Si cela n'avait pas été le cas, un objet de type colorpoint aurait été refusé en argument de origin.
Les deux premières formes de polymorphisme ont en commun
une propriété très
importante: une fonction polymorphe peut être appliquée
sans changement de son code à une infinité de types d'arguments.
Ainsi, le polymorphisme paramétrique et le polymorphisme
d'inclusion
sont des moyens de partager aussi bien
du code source que du code executable.
Ces deux formes de polymorphisme appartiennent
à un même catégorie
plus générale, dite polymorphisme universel.
Le polymorphisme ad-hoc se
distingue des deux formes précédentes
en ce que le code des fonctions n'est pas partagé.
En effet, un symbole surchargé se voit associer
autant de codes que de types distincts sur lesquels il veut
pouvoir s'appliquer. Cette forme de polymorphisme est donc
moins générale que le polymorphisme universel.
Polymorphisme | ||
![]() ![]() |
||
Universel | Ad-hoc | |
![]() ![]() |
![]() |
|
Paramétrique Sous-typage | Surcharge |