在ScottyT中使用ReaderT转换器(与ActionT相比)



我正在尝试使用 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的实例。

但是,我觉得我没有足够的法力和哈斯克尔经验来找到摆脱它的方法,并希望得到任何帮助!

谢谢!

通过集体思想,我们都找到了目前可行的问题解决方案。

ScottyT型外壳为单变压器,https://github.com/scotty-web/scotty/pull/167 合并后,因此目前没有办法以这种方式使用它。有一个公关 https://github.com/scotty-web/scotty/pull/181 旨在恢复该功能,但据我所知,它从未合并过。

由于它不是单元变压器,我们只能再次包装它:

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

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

最新更新