2

我一直在尝试让一个与 scotty 一起运行的 Web 服务器,它可以使用 selda 与我的数据库通信。我认为使用单子变压器堆栈将是完成类似事情的方法。我一直在努力解决它,但我遇到了一些死胡同,这些类型似乎不可行。

{-# 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

我试图将它从这里的答案松散地建立起来,但我想包装路由器而不是单独的路由。withPersist我不希望我打开的连接数与我拥有的路由数成正比,如果我可以避免的话,我不希望每条路由都必须有一个呼叫。

所以我有一个Appwhich 是 type SeldaT Sqlite ScottyM,并使用withPersist(即 == withSQLite "mydb.db"),我会把它SeldaT Sqlite ScottyM变成一个ScottyM. 不过问题还是挺多的,以下是我对它们的理解:

  • SeldaT m a受 约束(MonadIO m, MonadMask m),并且 ScottyM 没有MonadIO
  • Scotty.get返回 a ScottyM (),我觉得这是我lift用来把它变成 a 的地方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 变压器中。

4

1 回答 1

2

在查看了许多其他答案并了解了有关 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 转换器,因此他们也可以访问转换器堆栈
于 2019-12-07T16:52:58.333 回答