2013-04-09 3 views
1

Je teste un peu de code pour une petite expérience que je fais mais au début j'ai rencontré un barrage routier que je ne vois pas comment réparer.Type ambigu pour les membres de la fonction polymorphe GADT

data DatabaseDriver a b where 
    DatabaseDriver :: (Table table, Field field) => { 
     dbInsert :: table -> [field] -> String 
    , dbSelect :: table -> [field] -> String 
    } -> DatabaseDriver a b 

class Table a where 
    tableName :: a -> String 

class Field a where 
    fieldName :: a -> String 
    fieldValue :: a -> String 

psqlDriver = DatabaseDriver insert select 
    where 
     insert t fs = "insert into " ++ tableName t ++ " (" ++ fieldNames fs ++ ") values (" ++ fieldValues fs ++ ")" 
     select t fs = "select " ++ fieldNames fs ++ " from " ++ tableName t 
     fieldNames = joinComma fieldName 
     fieldValues = joinComma fieldValue 
     joinComma f = foldl (\a n -> a ++ ", " ++ n) "" . map f 

Ok, donc c'est un code de test, les fonctions du pilote obtiendraient beaucoup plus compliqué que cela, mais même dans ce test, je l'erreur « dans la contrainte « a0 » Type Ambigu variable: (Champ a0) Le compilateur voit donc que fieldName est appliqué à un champ mais apparemment il veut un type plus concret Ici, je suppose que le fait que les fonctions restent polymorphes fait que pgsqlDriver n'est pas une classe concrète?

Mais l'idée serait que ces fonctions soient polymorphes, c'est pourquoi j'ai choisi d'utiliser un GADT ici, donc je pourrais mettre des contraintes de type sur les paramètres de ces fonctions pilotes sans avoir à les répéter dans chaque instanciation . Le plan serait que le pilote de base de données défini pourrait fonctionner avec toutes les instances de champ et de table. Cela ne peut-il simplement pas être fait et mon type DatabaseDriver devrait également être une classe de type?

Répondre

3

Il existe trois options ici. La première option consiste à ajouter

{-# LANGUAGE NoMonomorphismRestriction #-} 

en haut de votre fichier de module.

La deuxième option consiste à ajouter une signature de type explicite à psqlDriver.

psqlDriver :: (Field field, Table table) => (table -> [field] -> String) -> DatabaseDriver a b 

La raison de tout cela est un peu nuancée, et se trouvent here

plus de détails La troisième option consiste à modifier la définition de psqlDriver à

psqlDriver = DatabaseDriver insert select 

Cependant, cette est vraiment ambigu - il n'y a pas de raison de préférer une instance particulière de Table ou Field sur une autre. Peut-être que vous voulez définir DatabaseDriver comme suit.

data DatabaseDriver table field where 
    DatabaseDriver :: (Table table, Field field) => { 
     dbInsert :: table -> [field] -> String 
    , dbSelect :: table -> [field] -> String 
    } -> DatabaseDriver table field 

Si la définition originale de DatabaseDriver est réécrite comme ADT, il est plus facile de comprendre pourquoi.

Comme il est actuellement écrit dans la question, la traduction à un ADT est

{-# LANGUAGE ExistentialQuantification #-} 

data DatabaseDriver a b 
    = forall table field . 
    (Table table, Field field) => DatabaseDriver 
    { dbInsert :: table -> [field] -> String 
    , dbSelect :: table -> [field] -> String 
    } 

Notez que le forall table field imbriqué, et comment table et field ont aucun lien avec a ou b.

La traduction destinée est soit

data DatabaseDriver table field 
    = (Table table, Field field) => DatabaseDriver 
    { dbInsert :: table -> [field] -> String 
    , dbSelect :: table -> [field] -> String 
    } 

ou très probablement

data DatabaseDriver table field 
    = DatabaseDriver 
    { dbInsert :: table -> [field] -> String 
    , dbSelect :: table -> [field] -> String 
    } 

Avoir les contraintes de classe de type dans la définition de DatabaseDriver ne vous permet pas de supprimer les contraintes de classe de type de l'utilisation de DatabaseDriver en particulier psqlDriver.Dans les deux traductions ADT ci-dessus, le type psqlDriver est

psqlDriver :: (Table table, Field field) => DatabaseDriver table field 
+0

Aucun de ces éléments ne semble fonctionner pour moi. Le pragma No restrictions mono nettoie les erreurs mais il montre toujours une erreur sur la définition et ajoute une signature qui ne nettoie pas. Mais je remarque que vous donnez une signature d'une fonction alors que psqlDriver n'est en fait qu'un enregistrement. Je pense que les enregistrements ne peuvent pas avoir de fonctions polymorphes car cela les rendrait "polymorphes" ce qui n'a pas de sens (je suppose?) Pour un type de données. – Jason

+0

Comme écrit actuellement, 'psqlDriver' n'est pas un enregistrement, mais une fonction. Il nécessite encore un deuxième argument, 'dbSelect'. Les enregistrements peuvent définitivement être polymorphes. Si vous voulez que 'psqlDriver' soit de type' forall a b. DatabaseDriver a b', vous devrez fournir un deuxième argument, éventuellement nommé 'select' (bien que ce nom n'ait pas d'importance, et je ne choisisse ce nom qu'en raison du nom actuel du premier argument' insert'). – ScootyPuff

+0

Je vois maintenant ce qui était prévu et j'ai modifié la réponse. – ScootyPuff

Questions connexes