Exponentiation en Haskell

Quelqu’un peut-il me dire pourquoi le prélude Haskell définit deux fonctions distinctes pour l’exponentiation (c.-à-d. ^ Et ** )? Je pensais que le système de caractères était censé éliminer ce type de duplication.

 Prelude> 2^2 4 Prelude> 4**0.5 2.0 

Il y a en fait trois opérateurs d’exponentiation: (^) , (^^) et (**) . ^ est une exponentiation intégrale non négative, ^^ est une exponentiation entière et ** est une exponentiation à virgule flottante:

 (^) :: (Num a, Integral b) => a -> b -> a (^^) :: (Fractional a, Integral b) => a -> b -> a (**) :: Floating a => a -> a -> a 

La raison en est la sécurité de type: les résultats des opérations numériques ont généralement le même type que les arguments en entrée. Mais vous ne pouvez pas élever une Int à une puissance en virgule flottante et obtenir un résultat de type Int . Et donc le type de système vous empêche de le faire: (1::Int) ** 0.5 produit une erreur de type. La même chose vaut pour (1::Int) ^^ (-1) .

Une autre manière de dire ceci: les types Num sont fermés sous ^ (ils ne doivent pas nécessairement avoir un inverse multiplicatif), les types Fractional sont fermés sous ^^ , les types Floating sont fermés sous ** . Comme il n’y a pas d’instance Fractional pour Int , vous ne pouvez pas l’augmenter.

Idéalement, le deuxième argument de ^ serait contraint statiquement à être non négatif (actuellement, 1 ^ (-2) émet une exception d’exécution). Mais il n’y a pas de type pour les nombres naturels dans le Prelude .

Le système de caractères de Haskell n’est pas assez puissant pour exprimer les trois opérateurs d’exponentiation en un seul. Ce que vous voulez vraiment, c’est quelque chose comme ceci:

 class Exp ab where (^) :: a -> b -> a instance (Num a, Integral b) => Exp ab where ... -- current ^ instance (Fractional a, Integral b) => Exp ab where ... -- current ^^ instance (Floating a, Floating b) => Exp ab where ... -- current ** 

Cela ne fonctionne pas vraiment même si vous activez l’extension de classe de type multi-parameters, car la sélection de l’instance doit être plus intelligente que ce que Haskell autorise actuellement.

Il ne définit pas deux opérateurs – il en définit trois! Du rapport:

Il existe trois opérations d’exponentiation à deux arguments: ( ^ ) élève n’importe quel nombre à une puissance entière non négative, ( ^^ ) élève un nombre fractionnaire à une puissance entière, et ( ** ) prend deux arguments à virgule flottante. La valeur de x^0 ou x^^0 est 1 pour tout x , y compris zéro; 0**y est indéfini.

Cela signifie qu’il existe trois algorithmes différents, dont deux donnent des résultats exacts ( ^ et ^^ ), tandis que ** donne des résultats approximatifs. En choisissant quel opérateur utiliser, vous choisissez l’algorithme à invoquer.

^ exige que son second argument soit un Integral . Si je ne me trompe pas, la mise en œuvre peut être plus efficace si vous savez que vous travaillez avec un exposant intégral. De plus, si vous voulez quelque chose comme 2 ^ (1.234) , même si votre base est une intégrale, 2, votre résultat sera évidemment fractionnaire. Vous avez plus d’options pour que vous puissiez contrôler plus étroitement quels types entrent et sortent de votre fonction d’exponentiation.

Le système de types de Haskell n’a pas le même objective que les autres systèmes de type, tels que ceux de C, Python ou Lisp. Le typage de canard est (presque) l’opposé de l’état d’esprit Haskell.