Exponentiation à Haskell

91

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

Prelude> 2^2
4
Prelude> 4**0.5
2.0
skytreebird
la source

Réponses:

130

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.5produit une erreur de type. Il en va de même (1::Int) ^^ (-1).

Une autre façon de dire ceci: les Numtypes sont fermés sous ^(ils ne sont pas obligés d'avoir un inverse multiplicatif), les Fractionaltypes sont fermés sous ^^, les Floatingtypes sont fermés sous **. Puisqu'il n'y a pas d' Fractionalexemple 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.

Mikhail Glushenkov
la source
31

Le système de types 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'instances doit être plus intelligente que ce que permet actuellement Haskell.

augustes
la source
4
L'affirmation selon laquelle ce n'est pas réalisable est-elle toujours vraie? IIRC, haskell a une option pour que le deuxième paramètre d'une classe de type multi-paramètre soit strictement déterminé par le premier paramètre. Y a-t-il un autre problème qui ne peut être résolu?
RussellStewart
2
@singular C'est toujours vrai. Le premier argument ne détermine pas le second, par exemple, vous voulez que l'exposant soit à la fois Intet Integer. 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.
augustss
7
L' argument «le système de types n'est pas assez puissant» tient-il toujours en mars 2015?
Erik Kaplun
3
Vous ne pouvez certainement pas l'écrire comme je le suggère, mais il pourrait y avoir un moyen de l'encoder.
août
2
@ErikAllik le fait probablement pour Haskell standard, car aucun nouveau rapport Haskell n'est sorti depuis 2010.
Martin Capodici
10

Il ne définit pas deux opérateurs - il en définit trois! À partir 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^0ou x^^0est 1 pour tout x, y compris zéro; 0**yn'est pas défini.

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.

Gabe
la source
4

^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. 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 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.

Dan Burton
la source
4
Je ne suis pas tout à fait d'accord pour dire que l'état d'esprit de type Haskell est le contraire du typage du canard. Les classes de type Haskell ressemblent beaucoup au typage canard. class Duck a where quack :: a -> Quackdéfinit ce que nous attendons d'un canard, puis chaque instance spécifie quelque chose qui peut se comporter comme un canard.
2011
9
@augustss Je vois d'où tu viens. Mais la devise informelle derrière la frappe de canard est «si cela ressemble à un canard, agit comme un canard et charlatan comme un canard, alors c'est un canard». Dans Haskell, ce n'est pas un canard à moins qu'il ne soit déclaré une instance de Duck.
Dan Burton
1
C'est vrai, mais c'est ce que j'attends de Haskell. Vous pouvez faire tout ce que vous voulez un canard, mais vous devez être explicite à ce sujet. Nous ne voulons pas confondre quelque chose que nous n'avons pas demandé pour être un canard.
2011
Il y a une différence plus spécifique entre la façon de faire Haskell et la frappe de canard. Oui, vous pouvez donner à n'importe quel type la classe de canard mais ce n'est pas un canard. C'est capable de charlataner, certes mais c'est quand même, concrètement, quel que soit le type. Vous ne pouvez toujours pas avoir de liste de canards. Une fonction qui accepte une liste de Ducks et qui mélange et associe différents types de classe Duck ne fonctionnera pas. À cet égard, Haskell ne vous permet pas de dire simplement "Si ça pique comme un canard, alors c'est un canard". Dans Haskell, tous vos canards doivent être des Quackers du même type. C'est en effet assez différent du typage du canard.
mmachenry
Vous pouvez avoir une liste de canards mixtes, mais vous avez besoin de l'extension de quantification existentielle.
Bolpat