How to use a monad stack with scotty and selda?
我一直在尝试使运行scotty的Web服务器可以使用selda与我的数据库进行通信。我认为使用monad变压器堆栈将是完成类似任务的方式。我一直在尝试解决问题,但遇到了一些死胡同,这些类型似乎根本无法工作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | {-# 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 |
我试图根据此处的答案来宽松地建立它,但是我想package路由器而不是单独的路线。我不希望我打开的连接数与我拥有的路由数成正比,如果可以避免的话,我也不希望每个路由都必须有一个
所以我有一个
-
SeldaT m a 受(MonadIO m, MonadMask m) 约束,并且ScottyM没有MonadIO 的实例 -
Scotty.get 返回一个ScottyM () ,我觉得这是我要使用lift 将其变成SeldaT Sqlite ScottyM 的地方,但是我遇到了一个错误,可能与ScottyM 不是一个实例有关从上方MonadIO 的。 -
由于
searchBook 仍然是ActionM ,因此我无法在其中运行查询。不确定如何获取get 来接受我的转换器堆栈而不是ActionM
以下是错误:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | /home/marcus/Documents/projects/nowwhatdoiread/nwdir-server/app/Server.hs:19:23: error: a€¢ No instance for (MonadIO ScottyM) arising from a use of a€?withPersista€? a€¢ In the second argument of a€?scottya€?, namely a€?(withPersist router)a€? In the expression: scotty 4200 (withPersist router) In an equation for a€?servera€?: server = scotty 4200 (withPersist router) | 19 | server = scotty 4200 (withPersist router) | ^^^^^^^^^^^^^^^^^^ /home/marcus/Documents/projects/nowwhatdoiread/nwdir-server/app/Server.hs:23:3: error: a€¢ No instance for (MonadTrans (SeldaT SQLite)) arising from a use of a€?lifta€? a€¢ 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 a€?routera€?: 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: a€¢ No instance for (MonadSelda (Web.Scotty.Internal.Types.ActionT Data.Text.Internal.Lazy.Text IO)) arising from a use of a€?querya€? a€¢ In a stmt of a 'do' block: books <- query selectBookQuery In the expression: do books <- query selectBookQuery json books In an equation for a€?searchBooka€?: searchBook = do books <- query selectBookQuery json books where selectBookQuery = do book <- select goodreadsBooks .... | 27 | books <- query selectBookQuery | ^^^^^^^^^^^^^^^^^^^^^ |
更新:在看了更多类似的问题之后,我可能需要使用ScottyT。但是不确定如何将SeldaT嵌套在ScottyT转换器中。
环顾了很多其他答案并了解了有关monad变压器的更多信息后,我才知道了这一点,我想到的解决方案是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | {-# 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转换器进行操作,因此他们也可以访问transformer堆栈