Incerteza
Incerteza

Reputation: 34874

Can't chain the functions for IO monad, have to use "do" instead

Could anyone explain why can't I do this:

getHeaders :: String -> IO ResponseHeaders
getHeaders url = parseUrl url >>= withManager . httpLbs >>= responseHeaders
--getHeaders url = parseUrl url >>= (\x -> withManager $ httpLbs x) >>= responseHeaders -- doesn't compiler either

and have to do this instead:

getHeaders url = do
  req <- parseUrl url
  res <- withManager $ httpLbs req
  return $ responseHeaders res

due to the error:

Couldn't match type `[]' with `IO'
    Expected type: Response Data.ByteString.Lazy.Internal.ByteString
                   -> IO ResponseHeaders
      Actual type: Response Data.ByteString.Lazy.Internal.ByteString
                   -> ResponseHeaders
    In the second argument of `(>>=)', namely `responseHeaders'
    In the expression:
      parseUrl url >>= withManager . httpLbs >>= responseHeaders

And is there any way to make it work with >>=, not do ?

update:

Indeed, adding return makes it work. However, I don't understand why I can chain them as they have different types of Monads. As far as I am concerned, they can't be chained with >>= because the return type of the left expression is not the same as the input the right expression accepts. Look:

http://hackage.haskell.org/package/http-conduit-1.2.4/docs/Network-HTTP-Conduit.html#v:parseUrl

parseUrl :: Failure HttpException m => String -> m (Request m')

withManager :: ResourceIO m => (Manager -> ResourceT m a) -> m a

Upvotes: 0

Views: 214

Answers (2)

bennofs
bennofs

Reputation: 11963

You forgot the return in your translation using >>=. The correct code is:

getHeaders url = parseUrl url >>= withManager . httpLbs >>= return . responseHeaders

This is required because (>>=) :: IO a -> (a -> IO b) -> IO b, but in your case, responseHeaders gives back a value of type ResponseHeaders, which doesn't match the IO b. With the return, you "lift" that value in the IO monad, so you get IO ResponseHeaders and the types match.

But, because a >>= return . f is equal to fmap f a for all f and a, this can also be written as:

parseUrl url = fmap responseHeaders $ parseUrl url >>= withManager . httpLbs

Upvotes: 7

Lee
Lee

Reputation: 144106

responseHeaders returns ResponseHeaders while you need to return an IO responseHeaders from the last call to >>=.

You can use:

getHeaders url = parseUrl url >>= withManager . httpLbs >>= (return . responseHeaders)

Upvotes: 1

Related Questions