我一直在尝试让一个与 scotty 一起运行的 Web 服务器可以使用 selda 与我的数据库进行通信。我认为使用 monad 变压器堆栈将是完成类似事情的方法。我一直在尝试解决这个问题,但我遇到了一些死胡同,其中的类型似乎不可行。
{-# LANGUAGE DeriveGeneric, OverloadedStrings, OverloadedLabels #-}
module Server where
import Web.Scotty
import Data.Monoid (mconcat)
import Data.Aeson (ToJSON)
import GHC.Generics
import Web.Scotty
import Database.Selda
import Database.Selda.SQLite
import Control.Monad.Trans.Class
import Models
type App = SeldaT SQLite ScottyM
-- withPersist:: (MonadIO m, MonadMask m) => SeldaT SQLite m a -> m a
server = scotty 4200 (withPersist router)
router :: App ()
router = do
lift $ get "/book/:id" searchBook
searchBook:: ActionM ()
searchBook = do
books <- query selectBookQuery
json books
where
selectBookQuery = do
book <- select goodreadsBooks
restrict (book ! #goodreadsId .== "20")
return book
我试图将其松散地建立在答案 here 的基础上。 ,但我想包装路由器而不是单独的路线。我不希望我打开的连接数与我拥有的路由数成正比,并且我不希望每条路由都必须有 withPersist
调用,如果我可以避免的话它。
所以我有一个App
,其类型为SeldaT Sqlite ScottyM
,并使用withPersist
(即==使用SQLite“mydb.db”
),我会将SeldaT Sqlite ScottyM
转换为ScottyM
。但问题还不少,以下是我的理解:
SeldaT m a
受(MonadIO m, MonadMask m)
约束,并且 ScottyM 没有MonadIO
的实例Scotty.get
返回一个ScottyM ()
,我觉得这就是我使用lift
将其变成SeldaT Sqlite ScottyM
,但我收到一个错误,可能与ScottyM
不是上面的MonadIO
的实例有关。- 由于
searchBook
仍然是ActionM
,因此我无法在其中运行查询。不确定如何让get
接受我的转换器堆栈而不是ActionM
错误如下:
/home/marcus/Documents/projects/nowwhatdoiread/nwdir-server/app/Server.hs:19:23: error:
• No instance for (MonadIO ScottyM)
arising from a use of ‘withPersist’
• In the second argument of ‘scotty’, namely ‘(withPersist router)’
In the expression: scotty 4200 (withPersist router)
In an equation for ‘server’:
server = scotty 4200 (withPersist router)
|
19 | server = scotty 4200 (withPersist router)
| ^^^^^^^^^^^^^^^^^^
/home/marcus/Documents/projects/nowwhatdoiread/nwdir-server/app/Server.hs:23:3: error:
• No instance for (MonadTrans (SeldaT SQLite))
arising from a use of ‘lift’
• In a stmt of a 'do' block: lift $ get "/book/:id" searchBook
In the expression: do lift $ get "/book/:id" searchBook
In an equation for ‘router’:
router = do lift $ get "/book/:id" searchBook
|
23 | lift $ get "/book/:id" searchBook
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/home/marcus/Documents/projects/nowwhatdoiread/nwdir-server/app/Server.hs:27:12: error:
• No instance for (MonadSelda
(Web.Scotty.Internal.Types.ActionT
Data.Text.Internal.Lazy.Text IO))
arising from a use of ‘query’
• In a stmt of a 'do' block: books <- query selectBookQuery
In the expression:
do books <- query selectBookQuery
json books
In an equation for ‘searchBook’:
searchBook
= do books <- query selectBookQuery
json books
where
selectBookQuery
= do book <- select goodreadsBooks
....
|
27 | books <- query selectBookQuery
| ^^^^^^^^^^^^^^^^^^^^^
更新:在查看了一些更多类似的问题后,我可能需要使用 ScottyT。但不确定如何将 SeldaT 嵌套在 ScottyT 变压器中。
最佳答案
在查看了很多其他答案并了解了有关 monad 变压器的更多信息后,我发现了这一点,我得出的解决方案是这样的:
{-# LANGUAGE DeriveGeneric, OverloadedStrings, OverloadedLabels #-}
module Server where
import Web.Scotty.Trans
import Database.Selda
import Database.Selda.SQLite
import Control.Monad.Trans.Class
import Control.Monad.Identity
import qualified Data.Text.Lazy as TL
import Models
server :: IO ()
server = scottyT 4200 withPersist router
router :: ScottyT TL.Text (SeldaT SQLite IO) ()
router = do
get "/book/:id" searchBook
searchBook:: ActionT TL.Text (SeldaT SQLite IO) ()
searchBook = do
books <- lift $ query selectBookQuery
json books
where
selectBookQuery = do
book <- select goodreadsBooks
restrict (book ! #goodreadsId .== "20")
return book
有几个键:
- Scotty 有自己的 monad 变压器 ScottyT,它必须是最外层的变压器
- 需要使用 Scott.Trans 来让转换器堆栈与 scotty 一起使用,因为类型更加通用
- 需要使用 ActionT 转换器来执行操作,因此他们也可以访问转换器堆栈
关于haskell - 如何与 scotty 和 Selda 一起使用 monad 堆栈?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59227783/