Sto lavorando su un server Haskell utilizzando scotty
e persistent
. Molti gestori hanno bisogno di accedere al pool di connessione al database, così ho preso a passare alla piscina intorno in tutta l'applicazione, in questa sorta di moda:Quando una funzione generica non è generica?
main = do
runNoLoggingT $ withSqlitePool ":memory:" 10 $ \pool ->
liftIO $ scotty 7000 (app pool)
app pool = do
get "/people" $ do
people <- liftIO $ runSqlPool getPeople pool
renderPeople people
get "/foods" $ do
food <- liftIO $ runSqlPool getFoods pool
renderFoods food
dove getPeople
e getFoods
sono opportune azioni persistent
di database che restituiscono [Person]
e [Food]
rispettivamente.
Il modello di chiamare liftIO
e runSqlPool
su una piscina diventa stancante dopo un po '- non sarebbe bello se potessi refactoring in una singola funzione, come Yesod di runDB
, che basta prendere la query e restituire il appropriata genere. Il mio tentativo di scrivere qualcosa di simile a questo è:
runDB' :: (MonadIO m) => ConnectionPool -> SqlPersistT IO a -> m a
runDB' pool q = liftIO $ runSqlPool q pool
Ora, posso scrivere questo:
main = do
runNoLoggingT $ withSqlitePool ":memory:" 10 $ \pool ->
liftIO $ scotty 7000 $ app (runDB' pool)
app runDB = do
get "/people" $ do
people <- runDB getPeople
renderPeople people
get "/foods" $ do
food <- runDB getFoods
renderFoods food
Solo che GHC si lamenta:
Couldn't match type `Food' with `Person'
Expected type: persistent-2.1.1.4:Database.Persist.Sql.Types.SqlPersistT
IO
[persistent-2.1.1.4:Database.Persist.Class.PersistEntity.Entity
Person]
Actual type: persistent-2.1.1.4:Database.Persist.Sql.Types.SqlPersistT
IO
[persistent-2.1.1.4:Database.Persist.Class.PersistEntity.Entity
Food]
In the first argument of `runDB', namely `getFoods'
Sembra GHC sta dicendo che in effetti il tipo di runDB
diventa specializzato in qualche modo. Ma come sono definite le funzioni come runSqlPool
? La sua firma di tipo simile al mio:
runSqlPool :: MonadBaseControl IO m => SqlPersistT m a -> Pool Connection -> m a
, ma può essere utilizzato con le query di database che restituiscono molti tipi diversi, come stavo facendo in origine. Penso che ci sia qualcosa di fondamentale in cui sto fraintendendo i tipi qui, ma non ho idea di come scoprire di cosa si tratta! Qualsiasi aiuto sarebbe molto apprezzato.
EDIT:
su suggerimento Yuras', ho aggiunto questo:
type DBRunner m a = (MonadIO m) => SqlPersistT IO a -> m a
runDB' :: ConnectionPool -> DBRunner m a
app :: forall a. DBRunner ActionM a -> ScottyM()
che ha richiesto -XRankNTypes
per il typedef. Tuttavia, l'errore del compilatore è ancora identico.
EDIT:
vittoria ai commentors. Questo permette al codice di compilare:
app :: (forall a. DBRunner ActionM a) -> ScottyM()
Per il quale sono grato, ma ancora disorientato!
Il codice è attualmente simile a this e this.
Prova ad aggiungere il tipo di firma a 'app' con' forall 'esplicito e molto probabilmente vedresti cosa c'è che non va. – Yuras
@Yuras Penso che parte di questo problema sia che attualmente ho una comprensione molto scarsa del 'forall' esplicito, ma cercherò di farlo. –
Suppongo che dovrebbe essere 'app :: (per sempre a. DBRunner ActionM a) -> ScottyM()'. –