Haskell 如何让ReaderT使用另一个monad转换器?

Haskell 如何让ReaderT使用另一个monad转换器?,haskell,monads,monad-transformers,Haskell,Monads,Monad Transformers,我想将ReaderT嵌入到另一个monad转换器中。我该怎么做?下面的示例使用了,但我认为它与任何其他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 imp

我想将
ReaderT
嵌入到另一个monad转换器中。我该怎么做?下面的示例使用了,但我认为它与任何其他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

您必须将作为
id
提供的参数更改为所有a的类型
。m a->n a
m Response->IO Response
。为什么?我不知道,但我发现的示例显示,有人运行与

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

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

好吧,这是可行的,但我不确定它是否正确:
flip runReaderT config$scottyT 3000 id(flip runReaderT config)routes
。考虑到
runReaderT
需要两次,我认为这是不正确的,但我对scotty了解不够,不知道为什么
scottyT
函数需要参数,你能不能改变“转换堆栈”的顺序,即
ReaderT配置(scottyT Text IO)(
?根据答案,你可以做'scottytt 3000'(flip runReaderT)(flip runReaderT)路由,其他人似乎就是这样做的。@chaosmatter不幸的是,没有。
get
函数的类型是
(ScottyError e,MonadIO m)=>RoutePattern->ActionT e m()->ScottyT e m()
,而不是更一般的
(ScottyError e,MonadIO m,MonadAction a,MonadScotty s)=>RoutePattern->a e m()->s e m()
@chaosmatter我不想这么做。我想能够不用抬就可以使用
Scotty
东西。如果有一个更容易理解的monad,它会是什么样子呢?看起来Scotty添加了一些魔法来实现这一点,但如果没有呢?这仍然是可能的吗?@seanclarkhes我通常看到的是,你会有类似
Scotty的东西T::(ScottyError e,MonadIO m)=>端口->ScottyT e m()->m()
,那么您可以通过只运行
scottyT
层来使用
flip runReaderT config$scottyT 3000 routes
。scotty似乎需要一些更高级的行为来执行操作,我发现很难相信他们仅仅因为糟糕的API设计就需要这些复杂的额外参数。就我个人而言,我会这样做我更愿意先运行
ReaderT
monad,留下
scotty
monad作为我的基础,只运行
scotty
,但这只是我对scotty的要求≥ 0.10,
scottyT
不再将“Run monad'm'转换为monad'n”,在“scottyT'级别”参数处调用一次,因此最后一行应该是
scottyT 3000 runner routes
main = do
    let config = Config "Hello, world"
        runner = flip runReaderT config
    scottyT 3000 runner runner routes