Le système d'inférence ne fait pas d'hypothèse, seulement des observations et une propagation de contraintes.
Dans le cas de Haskell, si tu écris une fonction pgcd, supposons comme ceci :
gcd'ab|b>0=gcd'b(modab)-- cas 1|otherwise=a-- cas 2
(Note, je l'appelle gcd' car gcd est déjà pris par une fonction qui fait la même chose... ;)
Le type se déduit de façon suivante :
il faut pouvoir appliquer (>) sur b et (>) est définie dans la typeclass Ord
il faut pouvoir appliquer mod a b, le type de mod est Integral t => t -> t -> t, donc on conclue que a et b sont dans la type classe Integral.
Integral étant plus restrictif que Ord, alors la seule contrainte c'est que a et b soient de type Integral
le type de retour de gcd' est soit le même que gcd' (cas 1), soit le même que a (cas 2).
On déduit donc le type suivant : gcd' :: Integral t => t -> t -> t.
À partir du moment où tu passes n'importe quel type qui implement Integral, alors tu n'auras pas de soucis. Si tu passes un autre type, cela ne marchera pas, mais bon, c'est normal, ton type ne sait pas faire...
Le seul problème ici c'est que, au sens de pas mal de monde, Integral est trop restrictif, puisque que pour implementer Integral il faut implement Integral et toutes ses sous typeclass, dont Real, Enum, Num, Ord et Eq avec, au minimum :
Integral : quotRem (div et mod en gros), toInteger
Real : toRational
Enum : toEnum, fromEnum
Num : (+), (*), abs, signum, fromInteger, negate ou (-)
Ord : (<=)
Eq : (==) ou (/=)
Ce qui fait beaucoup de bordel à implementer pour calculer un pgcd sur un type custom. La hiérarchie de type pour les nombres en Haskell est trop restrictive à mon gout, mais bon, personne n'est parfait ;) (En pratique cela ne pose rarement de problème sauf quand tu es un fou d'architecture par les types, mais à ce moment là Haskell n'est plus assez puissant pour tes besoins...)
De plus, il n'est pas obligatoire mais fortement conseillé de suivre des règles lors de l'implementation de ces classes (genre s'assurer que a + b == a - (negate b)).
Cependant, contrairement à de nombreux langage, ce mécanisme impose une certaine cohérence. Quand tu vois a / b, tu sais que c'est l’opérateur / de la classe Fractional et tu peux t'attendre a un comportement. Ce n'est pas le cas en python ou C++ ou le / peut être surchargé en n'importe quoi. Par example, certains s'en servent pour concaténer des chemins de fichiers. C'est une restriction mais j'ai appris à l'aimer car cela rend le code très explicite.
[^] # Re: A force...
Posté par Guillaum (site web personnel) . En réponse au journal Typage statique pour Python. Évalué à 4.
Le système d'inférence ne fait pas d'hypothèse, seulement des observations et une propagation de contraintes.
Dans le cas de Haskell, si tu écris une fonction pgcd, supposons comme ceci :
(Note, je l'appelle
gcd'cargcdest déjà pris par une fonction qui fait la même chose... ;)Le type se déduit de façon suivante :
(>)surbet(>)est définie dans la typeclassOrdmod a b, le type demodestIntegral t => t -> t -> t, donc on conclue queaetbsont dans la type classeIntegral.Integralétant plus restrictif queOrd, alors la seule contrainte c'est queaetbsoient de typeIntegralgcd'est soit le même quegcd'(cas 1), soit le même quea(cas 2).On déduit donc le type suivant :
gcd' :: Integral t => t -> t -> t.À partir du moment où tu passes n'importe quel type qui implement
Integral, alors tu n'auras pas de soucis. Si tu passes un autre type, cela ne marchera pas, mais bon, c'est normal, ton type ne sait pas faire...Le seul problème ici c'est que, au sens de pas mal de monde,
Integralest trop restrictif, puisque que pour implementerIntegralil faut implementIntegralet toutes ses sous typeclass, dontReal,Enum,Num,OrdetEqavec, au minimum :Integral:quotRem(div et mod en gros),toIntegerReal:toRationalEnum:toEnum,fromEnumNum:(+),(*),abs,signum,fromInteger,negateou(-)Ord:(<=)Eq:(==)ou(/=)Ce qui fait beaucoup de bordel à implementer pour calculer un pgcd sur un type custom. La hiérarchie de type pour les nombres en Haskell est trop restrictive à mon gout, mais bon, personne n'est parfait ;) (En pratique cela ne pose rarement de problème sauf quand tu es un fou d'architecture par les types, mais à ce moment là Haskell n'est plus assez puissant pour tes besoins...)
De plus, il n'est pas obligatoire mais fortement conseillé de suivre des règles lors de l'implementation de ces classes (genre s'assurer que
a + b == a - (negate b)).Cependant, contrairement à de nombreux langage, ce mécanisme impose une certaine cohérence. Quand tu vois
a / b, tu sais que c'est l’opérateur/de la classeFractionalet tu peux t'attendre a un comportement. Ce n'est pas le cas en python ou C++ ou le/peut être surchargé en n'importe quoi. Par example, certains s'en servent pour concaténer des chemins de fichiers. C'est une restriction mais j'ai appris à l'aimer car cela rend le code très explicite.