2014-09-26 74 views
2

我想嵌入ReaderT到另一个monad变压器。我该怎么做呢?下面的例子使用Scotty,但我认为它会与任何其他monad相同。如何让ReaderT与另一个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)() 
+0

嗯,这个工程,但我不知道它是正确的:'flip runReaderT config $ scottyT 3000 id(flip runReaderT config)routes'。我不认为这是正确的考虑'runReaderT'需要两次,但我不知道为什么scotty知道为什么'scottyT'函数需要这个参数 – bheklilr 2014-09-26 18:14:08

+0

你能否改变“Transformes stack”的顺序,即' ReaderT配置(ScottyT文本IO)()'? – chaosmasttter 2014-09-26 18:16:30

+0

基于[this](http://stackoverflow.com/a/23190718/839246)的答案,你可以做'scottyT 3000(flip runReaderT)(flip runReaderT)路线,而这似乎是其他人的方式完成了。 – bheklilr 2014-09-26 18:19:49

回答

1

你必须改变你作为id所提供的是那些有类型分别forall a. m a -> n am Response -> IO Response的参数。为什么?我不知道,但我发现here的范例显示某人运行它类似于

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

我测试过它,它至少起作用。我不知道这是否是最佳实践。如果有人有更好的方法,随时发布。

+0

它如何看起来更容易理解monad?它看起来像斯科蒂添加了一些魔术来实现这一点,但如果他们没有?它仍然有可能吗? – 2014-09-26 18:38:45

+0

@SeanClarkHess我通常看到的是,你会有'scottyT ::(ScottyError e,MonadIO m)=> Port - > ScottyT em() - > m()',那么你可以'翻转runReaderT config $ scottyT 3000 routes'而不是只运行'ScottyT'图层。似乎scotty需要一些更高级的行为来执行这些动作,我发现很难相信他们只需要糟糕的API设计就需要这些复杂的额外参数。就个人而言,我宁愿能够先运行'ReaderT' monad,留下一个'ScottyM' monad作为我的基础,以'scotty'运行,但这只是我 – bheklilr 2014-09-26 18:44:03

+1

对于Scotty≥0.10,'scottyT'不再需要“运行monad'm'为monad'n',在'ScottyT'level参数中调用一次,所以最后一行应该是'scottyT 3000 runner routes'。 – beta 2015-07-10 08:17:51