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?
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 ...
Pourquoi ne voulez-vous pas utiliser 'newtype'? – ErikR
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
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