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