web-dev-qa-db-fra.com

Exponentiation à Haskell

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

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

Il existe en fait trois opérateurs d'exponentiation: (^), (^^) Et (**). ^ Est l'exponentiation intégrale non négative, ^^ Est l'exponentiation entière et ** Est l'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 le ou les arguments d'entrée. Mais vous ne pouvez pas élever un Int à une puissance à virgule flottante et obtenir un résultat de type Int. Et donc le système de type vous empêche de faire ceci: (1::Int) ** 0.5 Produit une erreur de type. Il en va de même pour (1::Int) ^^ (-1).

Une autre façon de dire ceci: Num les types sont fermés sous ^ (Ils ne sont pas tenus d'avoir un inverse multiplicatif), Fractional les types sont fermés sous ^^ , Floating les types sont fermés sous **. Puisqu'il n'y a pas d'instance Fractional pour Int, vous ne pouvez pas la porter à une puissance négative.

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

121
Mikhail Glushenkov

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

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

Cela ne fonctionne pas vraiment même si vous activez l'extension de classe de type multi-paramètres, car la sélection d'instance doit être plus intelligente que Haskell ne le permet actuellement.

28
augustss

Il ne définit pas deux opérateurs - il en définit trois! Extrait 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 à n'importe quelle 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 n'est pas dé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 l'opérateur à utiliser, vous choisissez l'algorithme à invoquer.

10
Gabe

^ Requiert que son deuxième 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 avoir un contrôle plus strict sur les types qui entrent et sortent de votre fonction d'exponentiation.

Le système de types de Haskell n'a pas le même objectif que d'autres systèmes de types, tels que les C, Python ou LISP. La frappe de canard est (presque) l'opposé de l'état d'esprit Haskell.

4
Dan Burton