bitemyapp / esqueleto

New home of Esqueleto, please file issues so we can get things caught up!
BSD 3-Clause "New" or "Revised" License
376 stars 108 forks source link

tuple version of `deriveEsqueletoRecord` #371

Closed marcosh closed 1 year ago

marcosh commented 1 year ago

Currently, deriveEsqueletoRecord, used with a data type MyRecord, generate a new data type SqlMyRecord and the necessary instance instance SqlSelect SqlMyRecord MyRecord.

Would it make sense to have a similar thing to create just the SqlSelect instance to go from a tuple to the MyRecord data type?

In practice, given

data MyRecord = MyRecord
  { myName    :: 'Text'
  , myAge     :: 'Maybe' 'Int'
  , myUser    :: 'Entity' User
  , myAddress :: 'Maybe' ('Entity' Address)
  }

we could generate an instance

instance SqlSelect
  ( SqlExpr (Value Text))
  , SqlExpr (Value (Maybe Int))
  , SqlExpr (Entity User)
  , SqlExpr (Maybe (Entity Address))
  )
  MyRecord

which should allow then to write something like

getMyRecord :: SqlPersistT IO [MyRecord]
getMyRecord = select $ do
  user :& address <- from $
    table \@User
      `leftJoin`
      table @Address
      `on` (do \(user :& address) -> user ^. #address ==. address ?. #id)
  pure
    ( castString $ user ^. #firstName
    , val 10
    , user
    , address
    )

Would this be something useful which could be added to the library?

parsonsmatt commented 1 year ago

We can't do this because of the functional dependency - in current esqueleto, the functional dependency is bidirectional - so SqlSelect sql haskell means that there is a single haskell for any sql and vice versa. The functional dependency in esqueleto-next and 4.0 and beyond will be relaxed, to SqlSelect sql haskell | sql -> haskell. This means that each SQL thing will have exactly one Haskell representation. So if we had a SqlSelect sqlExprTuple MyRecord, then tuple wouldn't be allowed to convert a tuple of that size into a plain tuple.

Additionally a huge benefit of the sql record is getting to use records and field labels while inside of SQL code. Selecting into a Haskell record is cool but gettin to use named fields in esqueleto's EDSL is the real win IMO.

marcosh commented 1 year ago

Thanks @parsonsmatt for the explanation!