50 votes

Comment utiliser HaskellDB avec des champs polymorphes? (Problèmes avec des instances qui se chevauchent)

J'ai un schéma qui dispose de 6 types différents d'entités, mais elles ont toutes beaucoup de choses en commun. J'ai pensé que je pourrais probablement abstraite beaucoup de points communs au niveau du type, mais j'ai frappé un problème avec HaskellDB et de la superposition des instances. Voici le code que j'ai commencé avec, qui fonctionne très bien:

import Database.HaskellDB
import Database.HaskellDB.DBLayout

data Revision a = Revision deriving Eq
data Book = Book

instance FieldTag (Revision a) where
  fieldName _ = "rev_id"

revIdField :: Attr (Revision Book) (Revision Book)
revIdField = mkAttr undefined

branch :: Table (RecCons (Revision Book) (Expr (Revision Book)) RecNil)
branch = baseTable "branch" $ hdbMakeEntry undefined
bookRevision :: Table (RecCons (Revision Book) (Expr (Revision Book)) RecNil)
bookRevision = baseTable "book_revision" $ hdbMakeEntry undefined

masterHead :: Query (Rel (RecCons (Revision Book) (Expr (Revision Book)) RecNil))
masterHead = do
  revisions <- table bookRevision
  branches <- table branch
  restrict $ revisions ! revIdField .==. branches ! revIdField
  return revisions

Cela fonctionne bien, mais branch est trop spécifique. Ce que je veux exprimer est la suivante:

branch :: Table (RecCons (Revision entity) (Expr (Revision entity)) RecNil)
branch = baseTable "branch" $ hdbMakeEntry undefined

Cependant, avec ce changement, j'obtiens l'erreur suivante:

Overlapping instances for HasField
                            (Revision Book)
                            (RecCons (Revision entity0) (Expr (Revision entity0)) RecNil)
  arising from a use of `!'
Matching instances:
  instance [overlap ok] HasField f r => HasField f (RecCons g a r)
    -- Defined in Database.HaskellDB.HDBRec
  instance [overlap ok] HasField f (RecCons f a r)
    -- Defined in Database.HaskellDB.HDBRec
(The choice depends on the instantiation of `entity0'
 To pick the first instance above, use -XIncoherentInstances
 when compiling the other instance declarations)
In the second argument of `(.==.)', namely `branches ! revIdField'
In the second argument of `($)', namely
  `revisions ! revIdField .==. branches ! revIdField'
In a stmt of a 'do' expression:
      restrict $ revisions ! revIdField .==. branches ! revIdField

J'ai essayé de jeter aveuglément -XOverlappingInstances et -XIncoherentInstances à ce, mais ça n'aide pas (et j'aimerais vraiment comprendre pourquoi le remplacement d'un type de béton avec une variable de type causes de ce si problématique).

Une aide et des conseils seront très appréciés!

2voto

Geoff Reedy Points 16508

Avec l'âge de cette question, il est probablement trop tard pour une réponse à faire toute la différence pour vous, mais peut-être que si une autre personne vient avec un problème similaire...

Il s'agit du fait qu'elle ne peut être déduite que vous souhaitez entity pour désigner Book lorsque branch est utilisé en masterHead. La partie du message d'erreur qui lit

Le choix dépend de l'instanciation de la "entity0'

vous indique où vous en avez besoin pour supprimer l'ambiguïté, à savoir que vous avez besoin de donner plus d'informations à propos de ce qu' entity0 devraient l'être. Vous pouvez donner quelques annotations de type pour aider les choses.

Tout d'abord, définissez branch comme

type BranchTable entity = Table (RecCons (Revision entity) (Expr (Revision entity)) RecNil)
branch :: BrancTable entity
branch = baseTable "branch" $ hdbMakeEntry undefined

et puis le changement masterHead lire

masterHead :: Query (Rel (RecCons (Revision Book) (Expr (Revision Book)) RecNil))
masterHead = do
  revisions <- table bookRevision
  branches <- table (branch :: BranchTable Book)
  restrict $ revisions ! revIdField .==. branches ! revIdField
  return revisions

Remarque le type d'annotation appliquée à l' branch: branch :: BranchTable Book qui sert à éliminer l'ambiguïté qui est à l'origine du type d'erreur.

Pour faire masterHead applicable à quoi que ce soit avec un Revision e champ, vous pouvez utiliser cette définition:

masterHead :: (ShowRecRow r, HasField (Revision e) r) => Table r -> e -> Query (Rel r)
masterHead revTable et =
  do  revisions <- table revTable
      branches <- table branch'
      restrict $ revisions ! revIdField' .==. branches ! revIdField'
      return revisions
  where (branch', revIdField') = revBundle revTable et
        revBundle :: HasField (Revision e) r => Table r -> e -> (BranchTable e, Attr (Revision e) (Revision e))
        revBundle table et = (branch, revIdField)

L' et argument est nécessaire de préciser quel est le e type doit être et peut-être juste undefined attribuée à un type approprié comme dans

masterHead bookRevision (undefined :: Book)

qui génère le SQL

SELECT rev_id1 as rev_id
FROM (SELECT rev_id as rev_id2
      FROM branch as T1) as T1,
     (SELECT rev_id as rev_id1
      FROM book_revision as T1) as T2
WHERE rev_id1 = rev_id2

ceci n'exige FlexibleContexts , mais il peut être appliqué à la personne du module sans avoir à recompiler HaskellDB.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X