我怎样才能使用HaskellDB多态字段? (重叠实例的问题)

我有一个有六种不同types实体的模式,但是他们都有很多共同点。 我想我可能可以在types级别抽象出很多这种通用性,但是我碰到了HaskellDB和重叠实例的问题。 这是我开始的代码,它工作正常:

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 

这工作正常,但branch太具体了。 我真正想expression的是以下几点:

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

但是,随着这一变化,我得到以下错误:

 Overlapping instances for HasField (Revision Book) (RecCons (Revision entity0) (Expr (Revision entity0)) RecNil) arising from a use of `!' Matching instances: instance [overlap ok] HasField fr => HasField f (RecCons gar) -- Defined in Database.HaskellDB.HDBRec instance [overlap ok] HasField f (RecCons far) -- 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 

我已经尝试了盲目地抛出-XOverlappingInstances-XIncoherentInstances在这个,但是这并没有帮助(我想实际上理解为什么用一个typesvariablesreplace具体types导致这是如此的问题)。

任何帮助和build议将不胜感激!

随着这个问题的年龄,对你做出改变的答案可能已经太晚了,但也许如果有其他人出现了类似的问题…

事实上,在masterHead使用branch时,无法推断出您希望entity引用Book 。 读取的错误消息部分

select取决于“entity0”的实例化

告诉你在哪里需要消除模糊性,具体来说,你需要提供更多关于entity0应该是什么的信息。 你可以给一些types的注释来帮助解决问题。

首先,定义branch

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

然后改变masterHead来读取

 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 

注意应用于branch的types注释: branch :: BranchTable Book ,用于消除导致types错误的歧义。

为了使masterHead适用于任何带有Revision e字段的东西,你可以使用这个定义:

 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) 

et参数需要指定etypes应该是什么,可以将它undefined为正确的types

 masterHead bookRevision (undefined :: Book) 

生成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 

这确实需要FlexibleContexts ,但它可以应用到提问者的模块,而不用重新编译HaskellDB。