XII. Pièges du typage▲
Cette courte partie devrait vous donner une bonne intuition en ce qui concerne les problèmes que le système de type de Haskell pose aux débutants.
XII-A. Let-Bound Polymorphism▲
Tout langage utilisant le système de type de Hindley-Milner possède ce que l'on appelle let-bound polymorphism, parce que les identifiants non bornés par une clause let ou where, et non situés au niveau principal d'un module, sont limités par leur propre polymorphisme. En particulier, une fonction lambda-bound, c'est-à-dire qui est passée en argument à une autre fonction, ne peut pas être instanciée de deux manières différentes. Par exemple, le programme suivant est invalide :
let
f g =
(g [], g 'a'
) – ill-
typed expression
in
f (\x->
x)
En effet, le représentant d'une fonction abstraite, dont le type principal est a->a, est utilisée dans f de deux manières différentes : la première fois avec le type [a]->[a] ; et la seconde avec le type Char->Char.
XII-B. Surcharge numérique▲
Il est facile d'oublier que, parfois, les valeurs numériques sont surchargées, et pas implicitement converties dans les divers types numériques comme dans beaucoup d'autres langages. Ainsi des expressions numériques très générales ne peuvent parfois pas être généralisées. On rencontre souvent une erreur de typage pour des valeurs numériques comme celle-ci :
xs moyens =
xs de somme/
xs de longueur – Mal !
/ exige des arguments de type fraction, mais le résultat de length est de type Int. Cette erreur de type doit être corrigée en rendant le transtypage explicite :
average ::
(Fractional
a) =>
[a] ->
a
average xs =
sum xs /
fromIntegral (length xs)
XII-C. Les Restrictions monomorphiques▲
Le système de types de Haskell contient une restriction liée aux classes de type qui n'est pas présente dans le système de types standard de Hindley-Milner : la restriction de monomorphisme. La raison de cette restriction provient d'une subtile ambiguïté de types et est entièrement expliquée dans ce rapport. Pour faire court, on peut le résumer comme :
La restriction de monomorphisme indique que n'importe quel identifiant borné par un binding (cela inclut les bindings sur les identifiants seuls), et n'ayant aucun type explicite dans sa signature, doit être monomorphe. Un identifiant est monomorphe s'il n'est pas surchargé, ou bien s'il est surchargé, mais est employé de manière à n'obtenir au plus qu'une seule surcharge et n'est pas exporté.
Les violations de cette restriction ont comme conséquence une erreur de typage statique. La manière la plus simple d'éviter ce problème est de fournir un type explicite dans la signature. On peut remarquer que n'importe quel type mis en signature suffira, du moment qu'il s'agit d'un type correct.
Une violation courante de cette restriction survient lorsqu'on définit des fonctions d'ordre supérieur. En voici un exemple avec la définition classique de la somme :
sum =
foldl (+
) 0
On peut corriger cette erreur de typage statique, en ajoutant un type dans la signature, comme suit :
sum ::
(Num
a) =>
[a] ->
a
On remarque ainsi que le problème ne serait pas survenu si nous avions écrit ceci :
sum xs =
foldl (+
) 0
xs
En effet, la restriction n'est appliquée que dans les binding.