【问题标题】:How to get ReaderT to work with another monad transformer?如何让 ReaderT 与另一个 monad 转换器一起工作?
【发布时间】:2014-09-26 18:09:14
【问题描述】:

我想将ReaderT 嵌入到另一个monad 转换器中。我该怎么做呢?下面的示例使用Scotty,但我认为它与任何其他 monad 相同。

{-# LANGUAGE OverloadedStrings #-}

import qualified Web.Scotty
import Web.Scotty.Trans

import Data.Text.Lazy
import Control.Monad.IO.Class (liftIO)

import Control.Monad.Trans.Reader
import Control.Monad.Trans

data Config = Config Text

main :: IO ()
main = do
    let config = Config "Hello World"
    -- how to I make this line work?
    scottyT 3000 id id routes

routes :: ScottyT Text (ReaderT Config IO) ()
routes = do
    get "/" info

info :: ActionT Text (ReaderT Config IO) ()
info = do
    -- this part seems like it works!
    Config message <- lift ask
    text $ "Info: " `append` message

scottyT 3000 id id routes 行出现此错误,因为 scottyT 需要 ScottyT Text IO ()。我该如何进行这项工作?以下是当前的错误:

Server.hs: line 24, column 24:
  Couldn't match type `ReaderT Config IO' with `IO'
    Expected type: ScottyT Text IO ()
      Actual type: ScottyT Text (ReaderT Config IO) ()

【问题讨论】:

  • 好吧,这可行,但我不确定它是否正确:flip runReaderT config $ scottyT 3000 id (flip runReaderT config) routes。考虑到需要两次 runReaderT,我认为这是不正确的,但我对 scotty 的了解还不够,无法知道为什么 scottyT 函数需要该参数
  • 你能改变“转换堆栈”的顺序,即ReaderT Config (ScottyT Text IO) ()吗?
  • 基于this 的回答,你可以做`scottyT 3000 (flip runReaderT) (flip runReaderT) 路线,这似乎是其他人的做法。
  • @chaosmasttter 不幸的是,没有。 get 函数的类型为 (ScottyError e, MonadIO m) =&gt; RoutePattern -&gt; ActionT e m () -&gt; ScottyT e m (),而不是更通用的 (ScottyError e, MonadIO m, MonadAction a, MonadScotty s) =&gt; RoutePattern -&gt; a e m () -&gt; s e m ()
  • @chaosmasttter 我不想。我希望能够在不提升的情况下使用Scotty 的东西。

标签: haskell monads monad-transformers


【解决方案1】:

您必须将您提供为id 的参数更改为分别具有forall a. m a -&gt; n am Response -&gt; IO Response 类型的参数。为什么?我不知道,但我找到的示例 here 显示有人运行它类似于

main = do
    let config = Config "Hello, world"
        runner = flip runReaderT config
    scottyT 3000 runner runner routes

我已经测试过了,它至少可以工作。我不知道这是否是最佳实践。如果有人有更好的方法,请随时发布。

【讨论】:

  • 使用更易于理解的 monad 会怎样?看起来斯科蒂添加了一些魔法来实现这一点,但如果他们没有呢?还有可能吗?
  • @SeanClarkHess 我通常看到的是你会拥有类似scottyT :: (ScottyError e, MonadIO m) =&gt; Port -&gt; ScottyT e m () -&gt; m () 的东西,然后你可以拥有flip runReaderT config $ scottyT 3000 routes,而不是只运行ScottyT 层。似乎 scotty 需要一些更高级的行为来执行这些操作,我发现很难相信他们会因为糟糕的 API 设计而需要这些复杂的额外参数。就个人而言,我更希望能够先运行 ReaderT monad,然后将 ScottyM monad 作为我的基础,然后使用 scotty 运行,但这只是我
  • 对于 Scotty ≥ 0.10,scottyT 不再采用“Run monad 'm' into monad 'n', called once at 'ScottyT' level”参数,所以最后一行应该是 @987654333 @.
猜你喜欢
  • 2023-02-02
  • 2012-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-25
  • 2020-12-13
  • 2012-02-02
相关资源
最近更新 更多