Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split up PersistEntity #1321

Open
parsonsmatt opened this issue Sep 30, 2021 · 0 comments
Open

Split up PersistEntity #1321

parsonsmatt opened this issue Sep 30, 2021 · 0 comments

Comments

@parsonsmatt
Copy link
Collaborator

parsonsmatt commented Sep 30, 2021

Related to #1037 and #1239

Right now, PersistEntity has a ton of responsibilities. Here's the class definition (minus comments)

class 
    ( PersistField (Key record), ToJSON (Key record), FromJSON (Key record)
    , Show (Key record), Read (Key record), Eq (Key record), Ord (Key record)
    )
  => 
    PersistEntity record 
  where
    type PersistEntityBackend record

    data Key record
    keyToValues :: Key record -> [PersistValue]
    keyFromValues :: [PersistValue] -> Either Text (Key record)
    persistIdField :: EntityField record (Key record)

    entityDef :: proxy record -> EntityDef

    data EntityField record :: Type -> Type
    persistFieldDef :: EntityField record typ -> FieldDef
    toPersistFields :: record -> [SomePersistField]
    fromPersistValues :: [PersistValue] -> Either Text record

    data Unique record
    persistUniqueKeys :: record -> [Unique record]
    persistUniqueToFieldNames :: Unique record -> NonEmpty (FieldNameHS, FieldNameDB)
    persistUniqueToValues :: Unique record -> [PersistValue]

    fieldLens :: EntityField record field
              -> (forall f. Functor f => (field -> f field) -> Entity record -> f (Entity record))

    keyFromRecordM :: Maybe (record -> Key record)
    keyFromRecordM = Nothing

So, a PersistEntity record implies:

  • There's a specific PersistEntityBackend record that the type must be used with (probably can be deleted with Deprecate mpsGeneric #1250 )
  • There's a primary Key for the record.
    • That key can be converted into a [PersistValue], and parsed from a [PersistValue]
    • There's a specific EntityField that directly corresponds to that primary key (this is false for composite keys)
  • You can get an entityDef Proxy :: EntityDef for a given type.
  • There is an EntityField instance for the type.
  • You can convert the type into a [SomePersistField] and parse one out of a [PersistValue].
  • The record has uniqueness keys. Rethink Unique #1239
    • Uniqueness keys can be converted into a [PersistValue] (but not parsed - there's no [PersistValue] -> Either Text (Unique record)?)
    • Uniqueness keys have a NonEmpty (FieldNameHS, FieldNameDB) associated - like a FieldDef or EntityDef
    • Primary keys and Unique keys are not interchangeable.

This is too much.

Here's a sketch of how I'd like to refactor this. First, I want to have classes for things that can be rendered into a database with multiple columns, as well as a class for parsing them out.

class ToPersistValues a where
    toPersistValues :: a -> [PersistValue]

class FromPersistValues a where
    -- note that we may want this to be something like Either Text ([PersistValue], a)
    -- if the idea is that we parse multiple columns, then it makes sense to have left overs
    fromPersistValues :: [PersistValue] -> Either Text a    

We can pretty easily have an instance of PersistField a => ToPersistValues (Only a). And we can make tuples for parsing out lots of stuff.

In #1133 I talk about using prairie, which pulls out EntityField, persistFieldLens, and other things from PersistEntity. That may not be feasible until we get the separation of types for inserting/reading from the database. I'm not sure if that's necessary.

Then that leaves our Key and Unique stuff. #1239 I think I'll put most of my thoughts in there about how to resolve it.


So, I think, for a path forward:

  1. Implement the To/FromPersistValues classes and make them superclasses of PersistEntity.
  2. Figure out the uniqueness keys design
    a. Do we want to make that a superclass? It doesn't make sense for all things... it is perfectly possible in SQL to have a table without a primary key.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant