haskell - superposicion - superposición de pantalla samsung j7
¿Cómo puedo usar HaskellDB con campos polimórficos?(Problemas con la superposición de instancias) (1)
Tengo un esquema que tiene 6 tipos diferentes de entidades, pero todas tienen muchas cosas en común. Pensé que probablemente podría abstraer mucho de esta similitud en el nivel de tipo, pero me he topado con un problema con HaskellDB y las instancias superpuestas. Aquí está el código con el que comencé, que funciona 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
Esto funciona bien, pero la branch
es demasiado específica. Lo que realmente quiero expresar es lo siguiente:
branch :: Table (RecCons (Revision entity) (Expr (Revision entity)) RecNil)
branch = baseTable "branch" $ hdbMakeEntry undefined
Sin embargo, con este cambio, aparece el siguiente error:
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
Intenté lanzar ciegamente -XOverlappingInstances
y -XIncoherentInstances
en esto, pero eso no ayuda (y me gustaría entender realmente por qué el reemplazo de un tipo concreto con una variable de tipo hace que esto sea tan problemático).
¡Cualquier ayuda y consejo sería muy apreciado!
Con la edad de esta pregunta, es probable que sea demasiado tarde una respuesta para hacer una diferencia para ti, pero tal vez si otra persona viene con un problema similar ...
Todo se reduce al hecho de que no se puede inferir que desee que la entity
consulte el Book
cuando se usa la masterHead
en masterHead
. La parte del mensaje de error que dice
La elección depende de la instanciación de `entity0 ''
le dice dónde necesita eliminar la ambigüedad, específicamente que necesita dar más información sobre qué entity0
debería ser. Puede dar algunas anotaciones de tipo para ayudar.
Primero, defina la branch
como
type BranchTable entity = Table (RecCons (Revision entity) (Expr (Revision entity)) RecNil)
branch :: BrancTable entity
branch = baseTable "branch" $ hdbMakeEntry undefined
y luego cambiar masterHead
para leer
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
Tenga en cuenta la anotación de tipo aplicada a branch
: branch :: BranchTable Book
que sirve para eliminar la ambigüedad que causaba el error de tipo.
Para hacer que masterHead
aplique a cualquier cosa que tenga un campo de Revision e
, puede usar esta definición:
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)
El argumento et
se necesita para especificar qué tipo de e
debe ser y puede ser undefined
atribuido al tipo correcto como en
masterHead bookRevision (undefined :: Book)
que genera el 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
sin embargo, esto requiere FlexibleContexts
, pero se puede aplicar al módulo de asker sin recompilar HaskellDB.