haskell - 在 ScottyT 中使用 ReaderT 转换器(对比 ActionT)

标签 haskell monads monad-transformers scotty

我正在尝试使用 ReaderT monad 转换器方法通过我的基于 Scotty 的应用程序进行线程配置,但这样做时遇到了困难。我必须在定义路由(因为其中一些依赖于配置)和处理实际请求时使用配置。

后者在 ActionT 中工作得很好,但无论我如何尝试,我都无法在 ScottyT 中获得正确的类型。

这是我根据 Scotty GitHub 存储库的 ReaderT 示例编译的最小示例:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}

module Main where

import Control.Applicative
import Control.Monad.Reader (MonadIO, MonadReader, ReaderT, asks, lift, runReaderT)
import Data.Default.Class (def)
import Data.Text.Lazy (Text, pack)
import Prelude
import Web.Scotty.Trans (ScottyT, get, scottyOptsT, text, capture)

data Config = Config
  { environment :: String
  } deriving (Eq, Read, Show)

newtype ConfigM a = ConfigM
  { runConfigM :: ReaderT Config IO a
  } deriving (Applicative, Functor, Monad, MonadIO, MonadReader Config)

application :: ScottyT Text ConfigM ()
application = do
  get "/" $ do
    e <- lift $ asks environment
    text $ pack $ show e

  path <- lift $ asks environment
  get (capture path) $ do
    text $ pack $ "Hello, custom path"

main :: IO ()
main = scottyOptsT def runIO application where
  runIO :: ConfigM a -> IO a
  runIO m = runReaderT (runConfigM m) config

  config :: Config
  config = Config
    { environment = "Development"
    }

我收到的错误是:

• No instance for (Control.Monad.Trans.Class.MonadTrans
                     (ScottyT Text))
    arising from a use of ‘lift’
• In a stmt of a 'do' block: path <- lift $ asks environment

我查看了概述 ScottyT 类型的代码,实际上似乎没有为其定义 MonadTrans 的实例。

但是,我觉得我没有足够的法力和 Haskell 经验来找到解决方法,并且希望得到任何帮助!

谢谢!

最佳答案

凭借集体智慧,我们大家找到了当前可行的问题解决方案。

ScottyT 类型是一个 monad 转换器,带有 https://github.com/scotty-web/scotty/pull/167已合并,因此目前无法以这种方式使用它。有一个 PR https://github.com/scotty-web/scotty/pull/181旨在恢复该功能,但据我了解,它从未被合并。

由于它不是 monad 转换器,我们只能再次包装它:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}

module Main where

import Control.Applicative
import Control.Monad.Reader (MonadIO, MonadReader, ReaderT, asks, lift, runReaderT)
import Data.Default.Class (def)
import Data.Text.Lazy (Text, pack)
import Prelude
import Web.Scotty.Trans (ScottyT, get, scottyOptsT, text, capture)

data Config = Config
  { environment :: String
  } deriving (Eq, Read, Show)

newtype ConfigM a = ConfigM
  { runConfigM :: ReaderT Config IO a
  } deriving (Applicative, Functor, Monad, MonadIO, MonadReader Config)

application :: ConfigM (ScottyT Text ConfigM ())
application = do
  path <- asks environment

  return $
    get "/" $ do
      e <- lift $ asks environment
      text $ pack $ show e

    get (capture path) $          
      text $ pack $ "Hello, custom path"

runIO :: Config -> ConfigM a -> IO a
runIO c m = runReaderT (runConfigM m) c

main :: IO ()
main = do
  let config = Config { environment = "/path" }
  app <- runIO config application
  scottyOptsT def (runIO config) app

感谢大家帮助我,希望这能帮助另一个像我一样流浪的斯科蒂:)。

关于haskell - 在 ScottyT 中使用 ReaderT 转换器(对比 ActionT),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48286926/

相关文章:

haskell - QuickCheck Gen 不是单子(monad)

haskell - unsafeDupablePerformIO 和 accursedUnutterablePerformIO 有什么区别?

haskell - Monad 是替代品但不是 MonadPlus 的例子是什么?

haskell - 如何使免费的 monad 解释器递归?

haskell - 使用 Reader 扩展 ServerPartT Monad

haskell - 在复合 StateT/Maybe monad 中,如何采取任何一种成功的可能性?

具有约束的 Haskell 实例

Haskell——Rand monad 中的计算超时

haskell - 也许是一堆变压器里面的单子(monad)

haskell - 解释 GHC 堆配置文件中的悬崖边缘