I’m trying to create a generic function to fetch records by their IDs. Something like:
fetchGenericJson recordIds = do
records <- query @record
|> filterWhereIn (#id, recordIds)
|> fetch
map toJSON records
I want to call it like so:
fetchGenericJson @News recordIds
However, I’m not sure how to write the type signature for fetchGeneric
so that it works generically with any record type (e.g., News
, User
, etc.) and enforces the necessary type constraints.
What would be the correct type signature for fetchGeneric
?
1 Like
I’m trying to base the signature on ihp/IHP/Fetch.hs at 49d9e233cf003d6cc18515297d3861a0ce9a9d64 · digitallyinduced/ihp · GitHub
fetchGenericJson :: forall table model. (Table model, KnownSymbol table, FromRow model, ?modelContext :: ModelContext, FilterPrimaryKey table, model ~ GetModelByTableName table, GetTableName model ~ table, ToJSON model) => [Id' table] -> IO Value
fetchGenericJson recordIds = do
records <- query @model
|> filterWhereIn (#id, recordIds)
|> fetch
fmap toJSON records
But it doesn’t like it
ihp | Web/Controller/DataSyncManager.hs:83:16: error: [GHC-83865]
ihp | • Couldn't match type: [model]
ihp | with: IO a0
ihp | Expected: QueryBuilder table -> IO (IO a0)
ihp | Actual: QueryBuilder table
ihp | -> IO (FetchResult (QueryBuilder table) model)
ihp | • In the second argument of ‘(|>)’, namely ‘fetch’
ihp | In a stmt of a 'do' block:
ihp | records <- query @model |> filterWhereIn (#id, recordIds) |> fetch
ihp | In the expression:
ihp | do records <- query @model |> filterWhereIn (#id, recordIds)
ihp | |> fetch
ihp | fmap toJSON records
ihp | |
ihp | 83 | |> fetch
ihp | | ^^^^^
Got it to compile, but still not sure it’s right.
import Database.PostgreSQL.Simple.ToField
fetchGenericJson :: forall table model.
( Table model
, KnownSymbol table
, FromRow model
, ?modelContext :: ModelContext
, FilterPrimaryKey table
, model ~ GetModelByTableName table
, GetTableName model ~ table
, HasField "id" model (Id' table)
, ToField (PrimaryKey table)
, ToJSON model
) => [Id' table] -> IO Value
fetchGenericJson recordIds = do
records <- query @model
|> filterWhereIn (#id, recordIds)
|> fetch
pure (toJSON records)
Next is figuring how to call this function correctly
1 Like
kbu
March 14, 2025, 8:57am
4
I wish there were some shorthand for such sequences of constraints, so one could just do something like forall table model. (?modelContext :: ModelContext, ToJSON model, model ~ IsModelOfTable table) => [Id' table] -> IO Value
. But I guess that would just push the complexity elsewhere.
I had to fight with the compiler, but here’s what I got
fetchGenericJson :: forall model table.
( Table model
, table ~ GetTableName model
, model ~ GetModelByTableName table
, KnownSymbol table
, FromRow model
, ?modelContext :: ModelContext
, FilterPrimaryKey table
, HasField "id" model (Id' table)
, ToField (PrimaryKey table)
, ToJSON model
) => [Id' table] -> IO Value
fetchGenericJson recordIds = do
records <- query @model
|> filterWhereIn (#id, recordIds)
|> fetch
pure (toJSON records)
Now I want to call it with this (where ids
is [UUID]
)
json <- fetchGenericJson @News ids
In my code I have a Revision
table that holds the recordId
(the UUID
), so this is working
revisions <- query @Revision |> fetch
let ids = map recordId revisions |> map packId
json <- fetchGenericJson @News ids
renderJson json
2 Likes