2009-10-07 19 views
2

Je suis en train de compiler la fonction suivante dans Haskell pour imiter la différenciation d'un polynôme dont les constantes sont spécifiées dans une liste numérique:Pourquoi Haskell interprète-t-il mon type Num comme Enum?

diff :: (Num a) => [a] -> [a] 
diff [] = error "Polynomial unspecified" 
diff coeff = zipWith (*) (tail coeff) [0..] 

Haskell refuse de le compiler, me donner la raison suivante:

Could not deduce (Enum a) from the context (Num a) 
arising from the arithmetic sequence `0 .. ' at fp1.hs:7:38-42 
Possible fix: 
add (Enum a) to the context of the type signature for `diff' 
In the third argument of `zipWith', namely `[0 .. ]' 
In the expression: zipWith (*) (tail coeff) ([0 .. ]) 
In the definition of `diff': 
diff coeff = zipWith (*) (tail coeff) ([0 .. ]) 

Pourquoi Haskell traite-t-il la liste [0..] comme un type Enum et comment puis-je résoudre ce problème. Gardez à l'esprit que je veux profiter de l'évaluation paresseuse ici, d'où la liste infinie.

+1

Est-ce que 'diff [] = []' ou 'diff [_] = [0]' ne serait pas meilleur? Après tout, prendre le quatrième différentiel de x^2 + 1 devrait être une constante 0, pas une erreur. – ephemient

+0

@ephemient: Vous êtes aux yeux d'aigle! J'ai pris soin de l'exception de la liste singleton après avoir posté le problème. Le code devrait aussi lire '[1 ..]' plutôt que '[0 ..]' pour qu'il se différencie correctement. – Zaid

Répondre

8

[0..] est le sucre syntaxique pour enumFrom 0, défini dans la classe Enum. Parce que vous souhaitez générer une liste de a s avec [0..] le compilateur demande a pour être dans la classe Enum.

Vous pouvez ajouter le Enum a à la signature de type de la fonction ou le travail autour d'elle en générant un [0..] :: [Integer] et en utilisant fromInteger (qui est défini dans la classe Num) pour obtenir un [a] de cette:

diff :: (Num a) => [a] -> [a] 
diff [] = error "Polynomial unspecified" 
diff coeff = zipWith (*) (tail coeff) (map fromInteger [0..]) 
+2

Hmm, j'écrirais probablement '(itérer (+1) 0)' car c'est plus court, mais à la fin c'est pareil ... – ephemient

+1

Intéressant. Je pensais que c'était juste une liste infinie d'entiers. Les choses que vous apprenez ... – Zaid

+0

La chose amusante est que tout le problème aurait pu être résolu en omettant simplement la signature de type et laisser la machine d'inférence faire son travail. – Dario

7

Le type de diff correcte doit être

diff :: (Num a, Enum a) => [a] -> [a] 

parce que l'utilisation de [x..] requiert le type d'instancier Enum.

2

Voici un bref résumé de ce que le compilateur voit quand il regarde cette fonction:

  • [0 ..] est une liste de choses qui ont des instances Num et Enum. Il doit être un Num à cause du '0', et il doit être un Enum à cause du '..'
  • On me demande d'appliquer (*) aux éléments de coeff et [0 .. ] un par un. Puisque les deux arguments de (*) doivent être du même type et que [0 ..] a une instance pour Enum, coeff doit aussi avoir une instance pour Enum.
  • Erreur! La signature de type de diff mentionne seulement que coeff a une instance pour Num, mais j'ai déjà déterminé qu'il doit au moins avoir une instance pour Enum aussi.
+0

Cela explique pourquoi une autre fonction n'a pas généré cette erreur.Cette fonction impliquait un simple 'zip':' (a, b) <- zip coeff [0 ..] 'renvoie des tuples, qui peuvent avoir des types de données mélangés. – Zaid