The title here is a bit misleading, but the basic idea is that complex mutually-dependent tables and datatypes are not currently possible.
An example:
Foo
name Text
Bar
name Text
parent Parent
This schema will work fine if Parent is declared before the splice occurs. However, we run into a problem if we want the following:
data Parent = ParentFoo FooId | ParentBar BarId
deriving (Show, Read, Eq)
This datatype depends on the datatypes introduced by mkPersist, but mkPersist also depends on those datatypes. The natural (for an experienced programmer, anyway) approach is to include the declaration of Parent in the splice:
$(concat <$> sequence [ [d| data Parent = ParentFoo $(conT $ mkName "FooId")
| ParentBar $(conT $ mkName "BarId")
|]
, derivePersistField "Parent"
, mkPersist ps $(persistFileWith lowerCaseSettings modelFile)
])
This approach still has a problem though! Because persistFileWith returns the SqlType unadorned in the FieldDef, the compiler attempts to splice in the Parent datatype in the inner splice, which naturally fails.
This two-stage problem has been seen before, as is evidenced by the code in getSqlType in Database.Persist.TH, which has to work around the issue.
Better, I think, would be to return a lifted Exp, changing the type of the EntityDef spliced in by persistFileWith (and other functions that produce EntityDefs) from EntityDef SqlType to EntityDef SqlTypeExp, as this will allow the splicing of the SqlType to be delayed until the outer splice, where the new datatype is visible and able to be compiled mutually with the rest of the database schema.
The title here is a bit misleading, but the basic idea is that complex mutually-dependent tables and datatypes are not currently possible.
An example:
This schema will work fine if Parent is declared before the splice occurs. However, we run into a problem if we want the following:
This datatype depends on the datatypes introduced by
mkPersist, butmkPersistalso depends on those datatypes. The natural (for an experienced programmer, anyway) approach is to include the declaration ofParentin the splice:This approach still has a problem though! Because
persistFileWithreturns theSqlTypeunadorned in theFieldDef, the compiler attempts to splice in theParentdatatype in the inner splice, which naturally fails.This two-stage problem has been seen before, as is evidenced by the code in
getSqlTypeinDatabase.Persist.TH, which has to work around the issue.Better, I think, would be to return a lifted
Exp, changing the type of theEntityDefspliced in bypersistFileWith(and other functions that produceEntityDefs) fromEntityDef SqlTypetoEntityDef SqlTypeExp, as this will allow the splicing of theSqlTypeto be delayed until the outer splice, where the new datatype is visible and able to be compiled mutually with the rest of the database schema.