86 votes

L'exponentiation en Haskell

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

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

125voto

Mikhail Glushenkov Points 10348

Il existe en fait trois opérateurs d'exponentiation : (^) , (^^) y (**) . ^ est une exponentiation intégrale non négative, ^^ est une exponentiation entière, et ** est une exponentiation en 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é des types : 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 flottante et obtenir un résultat de type Int . Et donc le système de type vous empêche de le faire : (1::Int) ** 0.5 produit une erreur de type. Il en va de même pour (1::Int) ^^ (-1) .

Une autre façon de le dire : Num sont fermés sous ^ (il n'est pas nécessaire qu'ils aient un inverse multiplicatif), Fractional sont fermés sous ^^ , Floating sont fermés sous ** . Puisqu'il n'y a pas de Fractional instance pour Int vous ne pouvez pas l'élever à une puissance négative.

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

30voto

augustss Points 15750

Le système de types 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 ça :

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 ce que Haskell permet actuellement.

10voto

Gabe Points 49718

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 tout nombre à une puissance entière non négative, ( ^^ ) élève un nombre fractionnaire à une puissance entière quelconque, et ( ** ) prend deux arguments à virgule flottante. La valeur de x^0 o x^^0 est égal à 1 pour toute x y compris zéro ; 0**y est indéfinie.

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

4voto

Dan Burton Points 26639

^ exige que son deuxième argument soit un Integral . Si je ne me trompe pas, l'implémentation peut être plus efficace si vous savez que vous travaillez avec un exposant intégral. En outre, 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 disposez d'un plus grand nombre d'options, ce qui vous permet de contrôler plus étroitement 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 ceux de C, Python ou Lisp. Le typage en canard est (presque) l'opposé de l'état d'esprit de Haskell.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X