2016-07-30 7 views
1

Je suis un débutant à Haskell.Comment créer une instance de type de classe dans haskell?

Je cherche s'il y a moyen de créer une instance de type d'une classe.

Existe-t-il un moyen de faire fonctionner ce code sans utiliser de données ou newtype?

type N = ∀n. (n -> n) -> n -> n 

instance Printable N where 
     print :: N -> IO() 
     read :: String -> N 

Lorsque je tente de charger le module dans GHCi il me dit:

Illegal polymorphic or qualified type: N 
In the instance declaration for ‘Printable N’ 
+4

Pourquoi ne voulez-vous pas utiliser 'newtype'? – ErikR

+0

si j'utilise newtype ou data j'ai besoin d'écrire quelque chose comme: 'newtype N = N (n -> n) -> n -> n)' puis réécrire des fonctions comme '(+) (N a) (N b) = stuff' au lieu de '(+) = \ ab -> stuff' sans débobiner a ou b. – CheeseLollipop

+0

Imprédicativité est préférable d'être évitée dans le GHC actuel. Pas moyen de contourner cela mais d'utiliser 'newtype' ou' data'. (Recherchez également les coercions sécurisées, ce qui rend 'newtype's beaucoup plus simple à gérer dans certains cas). – chi

Répondre

5

Qu'est-ce que vous avez écrit semble qu'il y a beaucoup comme une déclaration de classe , pas une instance. Peut-être que vous vouliez dire cela?

class Printable n where 
    print :: n -> IO() 
    read :: String -> n 

Notez que le n doit être en minuscule, parce que c'est vous variable de type quantifiant la classe sur. Si vous voulez vraiment définir une instance, vous, eh bien, instanciern avec N:

instance Printable N where 
    print n = ... 
    read str = ... 

A ce stade, les signatures de type sont tous fixes (de la définition de la classe) et ce que vous devez write sont les liaisons réelles de ces fonctions, donc il doit être =, pas ::. La question est: pourquoi avez-vous besoin de votre propre classe quand même? Cela va seulement provoquer des conflits de noms avec les fonctions standard print et read qui sont déjà dans le prélude. Ce que vous devez effectivement faire est instancier ces classes standard avec votre type N, à savoir

instance Show N where 
    show n = ... 
instance Read N where 
    readsPrec _ str = ... 

Cela dit, pour se rendre à la question réelle que vous avez demandé: pas, il est impossible de définir instances sensiblement pour un type polymorphe comme ∀ n . (n->n) -> n->n. Comment le compilateur est-il supposé distinguer ces types plus spécifiques comme (Int->Int) -> Int->Int, ou plus généraux comme ∀ n m . (n->m) -> n->m? C'est plutôt désespéré. La correcte chose à faire est juste de l'envelopper dans un newtype; cela cache la quantification universelle et permet au compilateur de distinguer correctement N des autres types.

Alternativement, vous pouvez simplement écrire fonctions monomorphes qui acceptent/rendement N directement:

showChurch :: N -> String 
showChurch n = ... 
readsPrecChurch :: Int -> ReadS N 
readsPrecChurch _ str = ... 

En fait, ce dernier, on est déjà trop pour le système de type: le ReadS N est court pour

readsPrecChurch :: Int -> String -> [(∀ n . (n->n) -> n->n, String)] 

quantification universelle dans une liste? Oh oh. C'est un type imprédictif. GHC a une extension -XImpredicativeTypes, mais cela ne fonctionne pas vraiment.

Encore une fois, évitez simplement ces problèmes en n'utilisant pas ouvertement les types polymorphes.Il y a quelques bonnes utilisations pour les types de Rank-N (notamment les lentilles), mais la plupart du temps ils sont complètement exagérés et inutiles. Il n'y a certainement jamais une bonne raison d'utiliser des chiffres d'église comme ça.

newtype N = Church { getChurch :: ∀ n . (n->n) -> n->n } 

vous permettra de définir des instances arbitraires ou des fonctions sans problème. Et, en pratique, en faisant simplement

type N = Int 

avec toutes les instances standard qui viennent pour les entiers est bien sûr tout aussi bon ...

+0

Désolé, j'ai mal expliqué ma question, mais vous avez toujours répondu correctement, merci beaucoup! – CheeseLollipop