2017-02-01 5 views
3

J'essaie d'envelopper ma tête autour des GADTs, et je soupçonne qu'il se passe quelque chose de magique que je ne comprends pas.La fonction d'enregistrement de GADT échoue là où la déconstruction réussit

Considérez ce qui suit:

class C t 

data T a where 
    T :: (C a) => { getT :: a } -> T a 

f :: C a => a ->() 
f = undefined 

class D t where 
    g :: t a ->() 

instance D T where 
    g (T x) = f x 

Tout cela est bon et compile avec succès.

Considérons maintenant une définition instance légèrement différente T:

instance D T where 
    g x = f (getT x) 

Cela ressemble exactement la même chose que ci-dessus, mais il y a une erreur de compilation. Qu'est-ce qu'il se passe ici? Le type de données T n'a pas de variables existentielles, il a une contrainte simple pour son seul constructeur mais c'est tout.

+1

« Le point clé est que sur les GADTs modèle des causes de type raffinement correspondant. » - [Les utilisateurs GHC Guide] (https://downloads.haskell.org/ ~ ghc/8.0.1-rc2/docs/html/users_guide/glasgow_exts.html # généralisé-algébrique-types-de-données-gadgets). Lorsque vous utilisez la correspondance de modèle avec une équation ou 'case', le typechecker a une portée évidente dans laquelle affiner localement le type et ajouter des contraintes au contexte. L'accesseur d'enregistrement ne «fuit» pas les contraintes dans le contexte environnant. –

Répondre

7

Qu'est-ce qui se passe ici est que le match de modèle

g (T x) = f x 

raconte l'typechecker que vous avez satisfait la contrainte C a vous pouvez donc utiliser f. Sans la correspondance de modèle, vous n'introduisez jamais C a donc il ne peut pas satisfaire la contrainte.

Il fonctionne de telle sorte qu'une valeur T something :: T a inclut également le dictionnaire pour C a et le rend disponible lorsque le modèle correspond à T. Cependant, en utilisant getT ne vous donne aucun moyen d'obtenir le dictionnaire pour C a (comme vous pouvez le voir du type getT :: T a -> a).


Pour la postérité l'erreur est

• No instance for (C a) arising from a use of ‘f’ 
    Possible fix: 
    add (C a) to the context of 
     the type signature for: 
     g :: T a ->() 
• In the expression: f (getT x) 
    In an equation for ‘g’: g x = f (getT x) 
    In the instance declaration for ‘D T’ 
+0

En effet: le modèle correspondant à 'T 'force l'évaluation, ainsi le dictionnaire de classes de types peut être extrait. Au lieu de cela, une application telle que 'f (getT y)' ne force pas l'évaluation de 'y', puisque' f' pourrait être paresseux. Donc, pour accepter cela, le compilateur doit soit 1) évaluer l'argument et casser la paresse (les programmeurs seront étonnés de le découvrir!) Soit 2) procéder avec un "dictionnaire paresseux", qui évaluerait l'argument dès qu'il sera accédé (encore un peu étonnant). Il est préférable de rejeter le code (ou au moins d'introduire des types spéciaux pour ce comportement). – chi

+0

@chi, je ne suis pas si sûr que cela a un rapport avec l'évaluation. Plus que le constructeur 'T' contient effectivement le dictionnaire pour le' C a' tandis que 'getT 'extrait seulement le' a'. – adamse

+0

Eh bien, vous ne pouvez pas extraire le dictionnaire jusqu'à ce que vous réduisiez l'expression à WHNF et exposiez le constructeur 'T'. – chi