Il y a en fait trois opérateurs de exponentiation: (^)
, (^^)
et (**)
. ^
est une exponentiation intégrale non négative, une exponentiation ^^
entière et 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 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 an 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 cela: (1::Int) ** 0.5
produit une erreur de type. Il en va de même (1::Int) ^^ (-1)
.
Une autre façon de dire ceci: les Num
types sont fermés sous ^
(ils ne sont pas obligés d'avoir un inverse multiplicatif), les Fractional
types sont fermés sous ^^
, les Floating
types sont fermés sous **
. Puisqu'il n'y a pas d' Fractional
exemple 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)
lève une exception d'exécution). Mais il n'y a pas de type pour les nombres naturels dans le Prelude
.
Int
etInteger
. Pour pouvoir avoir ces trois déclarations d'instance, la résolution d'instance doit utiliser le backtracking, et aucun compilateur Haskell ne l'implémente.Il ne définit pas deux opérateurs - il en définit trois! À partir du rapport:
Cela signifie qu'il existe trois algorithmes différents, dont deux donnent des résultats exacts (
^
et^^
), tandis que**
des résultats approximatifs. En choisissant l'opérateur à utiliser, vous choisissez l'algorithme à invoquer.la source
^
exige que son deuxième argument soit unIntegral
. Si je ne me trompe pas, l'implémentation peut être plus efficace si vous savez que vous travaillez avec un exposant intégral. De plus, si vous voulez quelque chose comme2 ^ (1.234)
, même si votre base est une intégrale, 2, votre résultat sera évidemment fractionnaire. Vous disposez de plus d'options pour pouvoir 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 objectif que les autres systèmes de types, tels que C, Python ou Lisp. Le typage du canard est (presque) l'opposé de l'état d'esprit Haskell.
la source
class Duck a where quack :: a -> Quack
définit ce que nous attendons d'un canard, puis chaque instance spécifie quelque chose qui peut se comporter comme un canard.Duck
.