我正在使用scotty
和persistent
在Haskell服务器上工作。许多处理程序都需要访问数据库连接池,因此我采用了在整个应用程序中传递连接池的方式:
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
其中,getPeople
和getFoods
是适当的persistent
数据库操作,分别返回[Person]
和[Food]
。
在池上调用liftIO
和runSqlPool
的模式在一段时间后会变得令人厌烦-如果我可以将它们重构为一个函数,如Yesod的runDB
,它将只接受查询并返回适当的类型,这不是很好吗?我尝试写这样的东西是:
runDB' :: (MonadIO m) => ConnectionPool -> SqlPersistT IO a -> m a
runDB' pool q = liftIO $ runSqlPool q pool
现在,我可以这样写:
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
除了GHC抱怨:
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'
似乎GHC是在说,事实上,不知何故,runDB
的类型变得专业化了。但是像runSqlPool
这样的函数是如何定义的呢?它的类型签名看起来与我的相似:
runSqlPool :: MonadBaseControl IO m => SqlPersistT m a -> Pool Connection -> m a
但它可以用于返回许多不同类型的数据库查询,就像我最初所做的那样。我认为我在这里对类型有一些基本的误解,但我不知道如何找出它是什么!任何帮助都将不胜感激。
编辑:
在Yuras的建议下,我添加了以下内容:
type DBRunner m a = (MonadIO m) => SqlPersistT IO a -> m a
runDB' :: ConnectionPool -> DBRunner m a
app :: forall a. DBRunner ActionM a -> ScottyM ()
这需要类型定义函数的-XRankNTypes
。但是,编译器错误仍然是相同的。
编辑:
评论员们胜利了。这允许代码编译:
app :: (forall a. DBRunner ActionM a) -> ScottyM ()
对此我心存感激,但仍然感到困惑!
https://stackoverflow.com/questions/28341784
复制相似问题