Author: Christian.Queinnec@lip6.fr

## Polymorphisme et valeurs de retour des fonctions

Le polymorphisme paramétrique d'Objective CAML permet de définir des fonctions dont le type de retour est complètement indéterminé. Par exemple :

    # let id x = x ;;
    val id : 'a -> 'a = <fun>

Cependant, le type de retour dépend du type de l'argument. Ainsi, lorsque l'on applique la fonction `id` à un argument, le mécanisme d'inférence de types sait instancier la variable de type 'a. Donc, pour chaque utilisation particulière, le type de `id` peut être déterminé.
S'il n'en était pas ainsi, l'utilisation du typage statique fort, chargé d'assurer la sûreté d'exécution, n'aurait plus de sens. En effet, une fonction d'un type entièrement indéterminé comme 'a -> 'b autoriserait n'importe quelle conversion de type qui amènerait inévitablement à une erreur durant l'exécution puisque la représentation physique des valeurs de type différents ne sont pas les mêmes.

#### Apparente contradiction

Cependant, il est possible de définir dans le langage Objective CAML des fonctions dont le type de retour contient une variable de type n'apparaissant pas dans le type de ses arguments. Nous allons en considérer quelques exemples et voir pourquoi une telle possibilité n'est pas contradictoire avec le typage statique fort.
Voici un premier exemple :

    # let f x = [] ;;
    val f : 'a -> 'b list = <fun>

Cette fonction permet de construire une valeur polymorphe à partir de n'importe quoi :

    # f () ;;
    - : '_a list = []
    # f "n'importe quoi" ;;
    - : '_a list = []

Néanmoins, la valeur obtenue n'est pas entièrement indéterminée : il s'agit d'une liste. Elle ne pourra donc pas être utilisée n'importe où.
Voici trois exemples de fonctions dont le type est celui redouté 'a -> 'b :

    # let rec f1 x = f1 x ;;
    val f1 : 'a -> 'b = <fun>
    # let f2 x = failwith "n'importe quoi" ;;
    val f2 : 'a -> 'b = <fun>
    # let f3 x = List.hd [] ;;
    val f3 : 'a -> 'b = <fun>

Ces fonctions ne sont, en fait pas dangereuses, vis-à-vis de la sûreté d'exécution car il n'est pas possible de les utiliser pour construire une valeur : la première boucle, les deux dernières déclenchent une exception qui interrompt le calcul.
C'est également pour ne pas pouvoir définir de fonction de type 'a -> 'b que les nouveaux constructeurs d'exceptions ne peuvent pas avoir d'argument dont le type contient une variable.
En effet, si l'on pouvait déclarer une exception polymorphe `Poly_exn` de type 'a -> exn, on pourrait alors écrire la fonction :

    let f = function
       0 -> raise (Poly_exn false)
     | n -> n+1 ;;

La fonction `f` étant de type int -> int et `Poly_exn` étant de type 'a -> exn, on pourrait alors définir :

    let g n = try f n with Poly_exn x -> x+1 ;;

Cette fonction est également bien typée (puisque l'argument de `Poly_exn` peut être quelconque) et, dès lors, l'évaluation de `(g 0)` aboutirait à la tentative d'additionner un entier et un booléen !