haskell - 如何与 scotty 和 Selda 一起使用 monad 堆栈?

标签 haskell monad-transformers scotty

我一直在尝试让一个与 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/

相关文章:

haskell - 使用字符串内容调用同名函数

haskell - 状态和 IO 单子(monad)

haskell - 为什么有一个用于 exceptT 的 MonadMask 实例?

haskell - 在 Openshift 中部署 Haskell(独立、yesod、snap...)失败

haskell -斯科蒂 : Set custom headers (x-frame-options)

haskell - 遍历 'filtered'的最后一个元素

haskell - 将 Int 或 Integer 转换为 [Word8] 或 [Bit]

haskell - 为什么我们不用 LISP 语法编写 haskell 呢? (我们可以!)

haskell - Haskell 中的嵌套状态