4.2) La perte est très relative vu que la plupart de tes types de suites sont de toute façon polymorphiques en "a" et que tu ne peux donc mêler "a" et "Int=Index" sans conversion explicite de toute façon. Le newtype ici n'apporte pas assez comparé à son cout en complexité du code.
4.4) L'ensemble de tes problèmes provient de la définition de ta classe :
1 2 3 4 5 6 7 8
| class MathSeq s where
u_FirstTerm :: s a -> a
u_FirstIndex :: s a -> Index
recRelation :: s a -> (a -> a) -- function that compute u_n+1 from the value of u_n
u_All :: s a -> [a] -- infinite list of all terms
-- u_n :: Index -> s a -> a -- nth term
u_All seq = iterate (recRelation seq) (u_FirstTerm seq)
-- u_n (Index n) seq = (u_All seq) !! n |
Cette définition affirme que tu pourras toujours déclarer une instance de MathSeq pour "s" contenant absolument n'importe quel type "a". Or ce n'est pas le cas pour tes suites arithmétiques qui n'ont de sens que pour un "a" instance de Num.
Tu as donc "réglé" le problème en ajoutant une contrainte "Num a" aux méthodes de ta classe mais du coup tu en restreins la généralité : ta classe ne pourra plus être utilisée qu'avec des suites numériques, pas des suites de fonctions, ou d'ensembles ou... Toute suites parfaitement légitimes d'un point de vue mathématiques (note que ta classe se restreint déjà aux suites définies par récurrence simple mais c'est un autre problème).
Ce que tu veux vraiment faire, c'est donner la possibilité de ne déclarer des instances de ArithSeq que pour des "a" numériques mais en gardant la classe d'origine complètement polymorphique en le type des termes. Ce n'est pas possible avec ta déclaration d'origine car tu ne parles pas du tout de "a" dans "class MathSeq s where" donc il est impossible de contraindre quelque chose sur lequel tu n'as aucune prise.
Il existe deux solutions à ce problème : les classes à plusieurs paramètres associées aux dépendances fonctionnelles ou les familles de type. Je recommande les familles de type comme l'approche la plus moderne et à l'aspect plus fonctionnel. Ta classe devient alors :
1 2 3 4 5 6 7
| class MathSeq sa where
type Term sa :: *
u_FirstTerm :: sa -> Term sa
u_FirstIndex :: sa -> Index
recRelation :: sa -> (Term sa -> Term sa) -- function that compute u_n+1 from the value of u_n
u_All :: sa -> [Term sa] -- infinite list of Term sall terms
u_All seq = iterate (recRelation seq) (u_FirstTerm seq) |
J'ai changé le nom du paramètre en "sa" pour souligner le fait qu'on n'a plus affaire à un constructeur de type mais bien à un type concret, contenant a. "type Term sa :: *" déclare que pour chaque instance il faudra déclarer le type du contenu, sous le nom de de "Term sa". Cette classe est plus flexible que ton original, elle peut entre autre être instancié pour un type ne prenant pas de paramètre, spécialisé pour un type concret de termes, une suite de Double par exemple.
Une instance classique ressemblera à :
1 2 3 4 5
| instance MathSeq (Seq a) where
type Term (Seq a) = a
u_FirstTerm s = firstTerm s
u_FirstIndex s = firstIndex s
recRelation s = recRel s |
Peu de changement mais l'instance est maintenant "Seq a" au lieu de "Seq".
On voit que le "a" fait partie du paramètre, on a donc prise sur ce "a" et on peut par exemple le contraindre :
1 2 3 4 5
| instance (Num a) => MathSeq (ArithmeticSeq a) where
type Term (ArithmeticSeq a) = a
u_FirstTerm (ArithmeticSeq ft _ _) = ft
u_FirstIndex (ArithmeticSeq _ i _) = i
recRelation (ArithmeticSeq _ _ cd) = (+ cd) |
Il faudra rajouter cette extension au début du fichier :
{-# LANGUAGE TypeFamilies #-}
Cette extension fait partie de celles qui seront probablement incluses dans Haskell à terme mais pour l'instant...
4.5) Ton type ArithmeticSeq ne respecte pas vraiment le contrat de Num. On peut éviter les warnings, mais on est alors obligé de mettre des méthodes qui débouchent sur des erreurs à l'exécution :
1 2 3 4 5 6 7 8
| instance (Num a) => Num (ArithmeticSeq a) where
-- Toujours un problème avec (ft1 + ft2) si i1 /= i2
(+) (ArithmeticSeq ft1 i1 cd1) (ArithmeticSeq ft2 i2 cd2) = ArithmeticSeq (ft1 + ft2) (max i1 i2) (cd1 + cd2)
(negate) (ArithmeticSeq ft i cd) = ArithmeticSeq (- ft) i (-cd)
fromInteger n = ArithmeticSeq (fromInteger n) 0 0 -- faisons une suite constante
_ * _ = error "Le produit de deux suites arithmétiques n'est pas une suite arithmétique"
abs _ = error "La valeur absolue d'une suite arithmétique n'est pas toujours arithmétique"
signum _ = error "???" |
Ta suite de Syracuse a également un petit problème, elle divise par 2 lorsque le terme est impair (odd) au lieu de pair (even).
--
Jedaï
Partager