Request: relax `HasField` requirements
I would like to provide persistent
users with a HasField
instance on Entity
that would allow them to leverage the SymbolToField
class we auto-generate to have easy field access on Entity Foo
table representations.
This works nicely with esqueleto
's database representation, SqlExpr
:
-- | This instance allows you to use @record.field@ notation with GHC 9.2's
-- @OverloadedRecordDot@ extension.
--
-- Example:
--
-- @
-- -- persistent model:
-- BlogPost
-- authorId PersonId
-- title Text
--
-- -- query:
-- 'select' $ do
-- bp <- 'from' $ 'table' \@BlogPost
-- pure $ bp.title
-- @
--
-- This is exactly equivalent to the following:
--
-- @
-- blogPost :: SqlExpr (Entity BlogPost)
--
-- blogPost ^. BlogPostTitle
-- blogPost ^. #title
-- blogPost.title
-- @
-- There's another instance defined on @'SqlExpr' ('Entity' ('Maybe' rec))@,
-- which allows you to project from a @LEFT JOIN@ed entity.
--
-- @since 3.5.4.0
instance
(PersistEntity rec, PersistField typ, SymbolToField sym rec typ)
=>
HasField sym (SqlExpr (Entity rec)) (SqlExpr (Value typ))
where
getField expr = expr ^. symbolToField @sym
As it happens, this only works because SqlExpr
is not already a record. When I try to define this on Entity
, which is defined as Entity { entityKey :: Key rec, entityVal :: rec }
, I get this error:
instance
( SymbolToField sym ent typ
, PersistEntity ent
)
=>
HasField sym (Entity ent) typ
where
getField ent =
view (persistFieldLens (symbolToField @sym @ent @typ)) ent
/home/matt/Projects/persistent/persistent/Database/Persist/Class/PersistEntity.hs:249:5: error:
• Illegal instance declaration for ‘HasField sym (Entity ent) typ’
Entity has fields
• In the instance declaration for ‘HasField sym (Entity ent) typ’
|
249 | HasField sym (Entity ent) typ
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The obvious conflict is if SymbolToField "entityKey" rec typ
existed - then we'd have a duplicate/overlapping instance, and that would be bad. Otherwise, it seems like the functional dependencies on SymbolToField
should render this pretty safe.
class SymbolToField (sym :: Symbol) rec typ | sym rec -> typ where
symbolToField :: EntityField rec typ
I can pretty easily work-around this in the persistent
case by just generating the instances via TemplateHaskell
.
instance HasField "userName" (Entity User) String where
getField = view (persistFieldLens UserName)
But, if these are orphans, it'd be pretty annoying to disable the orphan warning flag for each module that defines persistent entities and opts in to this feature.