{"action":"reopened","issue":{"url":"https://api.github.com/repos/yesodweb/persistent/issues/147","labels_url":"https://api.github.com/repos/yesodweb/persistent/issues/147/labels{/name}","comments_url":"https://api.github.com/repos/yesodweb/persistent/issues/147/comments","events_url":"https://api.github.com/repos/yesodweb/persistent/issues/147/events","html_url":"https://github.com/yesodweb/persistent/issues/147","id":18590755,"number":147,"title":"Cannot create complex circular relationships between tables and datatypes","user":{"login":"scshunt","id":295133,"avatar_url":"https://avatars.githubusercontent.com/u/295133?v=3","gravatar_id":"","url":"https://api.github.com/users/scshunt","html_url":"https://github.com/scshunt","followers_url":"https://api.github.com/users/scshunt/followers","following_url":"https://api.github.com/users/scshunt/following{/other_user}","gists_url":"https://api.github.com/users/scshunt/gists{/gist_id}","starred_url":"https://api.github.com/users/scshunt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scshunt/subscriptions","organizations_url":"https://api.github.com/users/scshunt/orgs","repos_url":"https://api.github.com/users/scshunt/repos","events_url":"https://api.github.com/users/scshunt/events{/privacy}","received_events_url":"https://api.github.com/users/scshunt/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":11,"created_at":"2013-08-27T06:56:05Z","updated_at":"2015-08-10T19:55:30Z","closed_at":null,"body":"The title here is a bit misleading, but the basic idea is that complex mutually-dependent tables and datatypes are not currently possible.\r\n\r\nAn example:\r\n```\r\nFoo\r\n    name Text\r\nBar\r\n    name Text\r\n    parent Parent\r\n```\r\n\r\nThis schema will work fine if Parent is declared before the splice occurs. However, we run into a problem if we want the following:\r\n\r\n```haskell\r\ndata Parent = ParentFoo FooId | ParentBar BarId\r\n    deriving (Show, Read, Eq)\r\n```\r\n\r\nThis 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:\r\n\r\n```haskell\r\n$(concat <$> sequence [ [d| data Parent = ParentFoo $(conT $ mkName \"FooId\")\r\n                                        | ParentBar $(conT $ mkName \"BarId\")\r\n                        |]\r\n                      , derivePersistField \"Parent\"\r\n                      , mkPersist ps $(persistFileWith lowerCaseSettings modelFile)\r\n                      ])\r\n```\r\n\r\nThis 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.\r\n\r\nThis 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.\r\n\r\nBetter, I think, would be to return a lifted `Exp`, changing the type of the `EntityDef` spliced in by `persistFileWith` (and other functions that produce `EntityDef`s) 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.\r\n"}}