• Aucun résultat trouvé

Fonctionnelles et polymorphisme

4.1 Notion de polymorphisme

´

Etymologiquement, polymorphe signifie plusieurs (poly) formes (morphe). On em- ploie ce mot par exemple en psychologie pour parler de pervers polymorphes (pervers qui ne sont pas fix´es sur une forme pr´ecise de perversion, ce qui est un stade normal de d´eveloppement psychologique de l’enfant), ou bien en m´edecine pour des maladies qui entraˆınent des symptˆomes variables, ou des virus dont l’aspect varie. En informa- tique, ce terme d´esigne des objets ou des programmes qui peuvent servir sans modifi- cations dans des contextes tr`es divers. Par exemple, une fonction de tri d’objets sera monomorphe si elle ne s’applique qu’`a un seul type d’objets (par exemple les entiers) et polymorphe si elle s’applique `a tous les types d’objets qu’on peut comparer pour les ranger du plus petit au plus grand. Dans ce dernier cas, le mˆeme programme de tri s’appliquera sans modifications `a des entiers (comparaison ≤), `a des nombres flottants (comparaison ≤ des flottants) et `a des chaˆınes de caract`eres (ordre du dictionnaire). Du point de vue du typage, cela signifie que la fonction de tri pourra ˆetre employ´ee avec plusieurs types diff´erents.

Le polymorphisme n’est pas l’apanage des fonctions : certaines valeurs non fonc- tionnelles peuvent aussi ˆetre utilis´ees avec plusieurs « formes », c’est-`a-dire plusieurs types. Les exemples se trouvent du cˆot´e des structures de donn´ees comme les tableaux et les listes : clairement, un tableau de nombres entiers ne pourra pas ˆetre employ´e avec un autre type, mais vous admettrez facilement que le tableau vide (le tableau `a z´ero ´el´ement) peut ˆetre vu comme un tableau d’entiers aussi bien que comme un tableau

de chaˆınes. Nous allons nous int´eresser d’abord aux fonctions polymorphes, parce que c’est dans le domaine des fonctions que cette notion est la plus naturelle et la plus facile `

a appr´ehender.

Pour exprimer le polymorphisme dans les expressions de types, nous avons besoin d’une notion de types qui puissent remplacer plusieurs types diff´erents : ce sont les param`etres de type, qu’on distingue syntaxiquement des types ordinaires en les faisant pr´ec´eder d’une apostrophe (’). Par exemple, ’a est un param`etre de type nomm´e a.

Le polymorphisme de Caml est techniquement qualifi´e de param´etrique. Intuitive- ment cela signifie que ce polymorphisme fonctionne en « tout ou rien ». La signification d’un param`etre de type est de remplacer n’importe quel autre type et non pas un certain nombred’autres types. On n’aura donc pas de programmes Caml uniquement valables pour un ensemble d´etermin´e de types. Par exemple, il n’y a aucun moyen de d´efinir une fonction qui s’appliquerait uniquement `a des entiers et des chaˆınes de caract`eres (et qui aurait donc un type du genre (int ou string) -> ...) Un programme Caml s’applique soit `a tous les types possibles, soit `a un seul et unique type. Dans le premier cas le type du programme comporte un param`etre (par exemple ’a -> ...), dans le second cas il n’en comporte pas (par exemple int -> ...). Voyons un premier exemple :

# let successeur x = x + 1;; successeur : int -> int = <fun>

La fonction est monomorphe, comme on s’y attend : elle ne s’applique qu’`a des entiers, puisqu’on doit faire une addition avec son argument. Mais supposons qu’on supprime l’addition qui entraˆıne cette contrainte sur l’argument x et qu’on renvoie directement 1.

# let fonction_un x = 1;; fonction_un : ’a -> int = <fun>

La fonction fonction_un est maintenant polymorphe : elle ne fait rien de son argument, on peut donc l’appliquer `a n’importe quoi.

# fonction_un 2;; - : int = 1 # fonction_un "oui";; - : int = 1 # fonction_un true;; - : int = 1

Contrairement `a ce que sugg`ere l’exemple fonction_un, une fonction polymorphe peut utiliser son argument, par exemple en le renvoyant tel quel. Nous supprimons encore une fois l’addition dans le code de successeur, mais cette fois nous renvoyons x au lieu de 1 :

# let identit´e x = x;; identit´e : ’a -> ’a = <fun>

Nous obtenons encore une fonction polymorphe. Notez que le type de la fonction identit´e indique `a juste titre que le type du r´esultat est exactement celui de l’argument. Le param`etre ’a remplace n’importe quel type, en particulier string ou int, l´egitimant ainsi l’emploi de identit´e avec le type string -> string, et aussi avec le type int -> int :

# identit´e "non";; - : string = "non"

# identit´e 1;; - : int = 1

Fonctions d’ordre sup´erieur 59 Ce m´ecanisme de remplacement d’un param`etre de type par un type quelconque s’appelle la sp´ecialisation. Nos deux exemples consistent donc `a sp´ecialiser ’a en string, puis en int. On n’est pas oblig´e de sp´ecialiser un param`etre avec un type de base, comme nous l’avons fait jusqu’`a pr´esent ; on le sp´ecialise tout aussi bien avec un type complexe, par exemple int -> int. Dans le cas de la fonction identit´e, on obtient le type (int -> int) -> (int -> int). Cela sugg`ere d’appeler la fonction identit´esur un argument qui est lui-mˆeme une fonction ; et pourquoi pas la fonction successeur?

# identit´e successeur;; - : int -> int = <fun>

La fonction identit´e renvoie toujours son argument sans modification ; elle renvoie donc tout simplement la fonction successeur quand on l’applique `a successeur. Par exemple :

# let success = identit´e successeur;; success : int -> int = <fun>

# success 3;; - : int = 4