我在理解此编译错误时遇到很多麻烦。代码如下:
#!/usr/bin/env stack
-- stack script --resolver lts-8.22
{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Simple
import qualified Data.ByteString as BS
import Control.Monad.Reader
import Control.Monad.Except
import Control.Monad.Trans.Class
import Data.Conduit
type Cmd i o = ReaderT (BS.ByteString) (ExceptT HttpException (ConduitM i o IO))
runCmd :: Cmd i o a -> BS.ByteString -> ConduitM i o IO (Either HttpException a)
runCmd cmdTS host = runExceptT $ runReaderT cmdTS host
readHost :: Cmd () BS.ByteString ()
readHost = do
host <- ask
let req = setRequestMethod "GET"
$ setRequestHost host
$ defaultRequest
lift . lift $ httpSource req getResponseBody
基本上,我有4层的MT堆栈,在readHost
中,我试图将对象从第二层提升到底层,再到顶层,因此我将两个lift
组合在一起。但是遗憾的是代码无法编译,并且出现以下错误:
[-Wdeferred-type-errors]
• No instance for (Control.Monad.Trans.Resource.Internal.MonadResource
IO)
arising from a use of ‘httpSource’
• In the second argument of ‘($)’, namely
‘httpSource req getResponseBody’
In a stmt of a 'do' block:
lift . lift $ httpSource req getResponseBody
In the expression:
do host <- ask
let req
= setRequestMethod "GET" $ setRequestHost host $ defaultRequest
lift . lift $ httpSource req getResponseBody
请帮助我了解此错误消息的含义。顺便说一句,我还没有看到有人用ConduiT
放在底部来构建这样的堆栈,所以我猜这是不好的做法吗?在这里什么是好的“设计模式”?我知道haskell中不存在“设计模式”一词,但我想不出更好的词。非常感谢!
答案 0 :(得分:2)
我首先减少monad变压器堆栈中的层以缩小您的 代码:
#!/usr/bin/env stack
-- stack script --resolver lts-8.22
{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Simple
import qualified Data.ByteString as BS
import Control.Monad.Reader
import Control.Monad.Except
import Control.Monad.Trans.Class
import Data.Conduit
type Cmd i o = ConduitM i o IO
readHost :: BS.ByteString -> Cmd () BS.ByteString ()
readHost host = do
let req = setRequestMethod "GET" . setRequestHost host $ defaultRequest
httpSource req getResponseBody
然后我运行了stack {scriptname}
,并得到了与您得到的错误非常相似的错误
对于较大的堆栈:
{scriptname}:17:3: error:
• No instance for (resourcet-1.1.9:Control.Monad.Trans.Resource.Internal.MonadResource
IO)
arising from a use of ‘httpSource’
• In a stmt of a 'do' block: httpSource req getResponseBody
In the expression:
do { let req
= setRequestMethod "GET" . setRequestHost host $ defaultRequest;
httpSource req getResponseBody }
In an equation for ‘readHost’:
readHost host
= do { let req = ...;
httpSource req getResponseBody }
弄清楚这种较小的情况,很可能会使版本更复杂 更容易解决。
让我们看一下httpSource
的类型:
httpSource :: (MonadResource m, MonadIO n) =>
Request
-> (Response (ConduitM i ByteString n ()) -> ConduitM i o m r)
-> ConduitM i o m r
其返回类型为ConduitM i o m r
,其中m
必须是
MonadResource
。
现在让我们再次看一下readHost
的最终返回类型。
Cmd () BS.ByteString ()
就是什么
ConduitM () BS.ByteString IO ()
变相。
比较Conduit () BS.ByteString IO ()
与ConduitM i o m r
可以看到
m
与IO
相对应。
IO
是MonadResource
的实例吗?根据GHC的说法,不是。
使用Hoogle环顾四周,让我们知道ResourceT IO
是
MonadResource
的实例。让我们在程序中尝试一下。
-- stack script --resolver lts-8.22
{-# LANGUAGE OverloadedStrings #-}
import Network.HTTP.Simple
import qualified Data.ByteString as BS
import Control.Monad.Reader
import Control.Monad.Except
import Control.Monad.Trans.Class
import Control.Monad.Trans.Resource
import Data.Conduit
main :: IO ()
main = putStrLn "hello"
type Cmd i o = ConduitM i o (ResourceT IO)
readHost :: BS.ByteString -> Cmd () BS.ByteString ()
readHost host = do
let req = setRequestMethod "GET" . setRequestHost host $ defaultRequest
httpSource req getResponseBody
这将正确编译,并打印到标准输出。
我相信您可以对较大的程序使用此修复程序。至少可以解释该错误。