haskellhttp-conduit

ResumableSource in http conduit results in "Cannot construct an infinite type" error


I am trying to create a function that creates a Source given some parameters to connect to a URL while being correctly in the ResourceT monad. I am trying the following:

{-# LANGUAGE OverloadedStrings #-}

import           Control.Monad.IO.Class       (liftIO)
import           Data.Conduit                 (($$), yield, unwrapResumable)
import qualified Data.Conduit.List            as CL
import           Network.HTTP.Conduit
import           Network.HTTP.Types           (methodPost)
import Control.Monad.Trans.Resource (runResourceT)

runquery manager  = do
  initreq <- parseUrl "http://localhost/test"
  let request = initreq{method=methodPost, requestBody=RequestBodyLBS "test"}
  response <- http request manager
  (httpsource, finalizer) <- unwrapResumable (responseBody response)
  httpsource
  finalizer

main = do
  manager <- newManager conduitManagerSettings
  runResourceT $ (runquery manager $$ CL.mapM_ (liftIO . print))

It does not work, I get 'cannot construct the infinite type' error from the compiler. I can return (httpsource,finalizer) (or just the whole responseBody) and use it later, but it seems to me strange. What is the proper way to write this code and why do I get the infinite type error?

Without type signature, I get the following error:

test.hs:17:3:
    Occurs check: cannot construct the infinite type:
      m
      ~
      conduit-1.2.4:Data.Conduit.Internal.Conduit.ConduitM
        () ByteString m
    Expected type: m ()
      Actual type: Source m ByteString
    Relevant bindings include
      finalizer :: m () (bound at test.hs:16:16)
      httpsource :: Source m ByteString (bound at test.hs:16:4)
      response :: Response
                    (conduit-1.2.4:Data.Conduit.Internal.Conduit.ResumableSource
                       m ByteString)

When I add a signature, which (I hope should be):

runquery :: Manager -> Source (ResourceT IO) ByteString

I get an error:

Couldn't match type ‘conduit-1.2.4:Data.Conduit.Internal.Conduit.ConduitM
                       () ByteString (ResourceT IO)’
              with ‘ResourceT IO’
Expected type: conduit-1.2.4:Data.Conduit.Internal.Conduit.ConduitM
                 () ByteString (ResourceT IO) ()
  Actual type: Source
                 (conduit-1.2.4:Data.Conduit.Internal.Conduit.ConduitM
                    () ByteString (ResourceT IO))
                 ByteString

I am probably doing something that couldn't be done, but I cannot quite see where is the problem.


Solution

  • unwrapResumable, http, and the finalizer need to run in a resourceT appear to only work in a MonadResource context, (such as resourceT), so you have to lift them into it.

    runquery :: MonadResource m => Manager -> Source m ByteString
    -- or runquery :: Manager -> Source (ResourceT IO) ByteString
    runquery manager  = do
      initreq <- parseUrl "https://localhost/test"
      let request = initreq{method=methodPost, requestBody=RequestBodyLBS "test"}
      response <- lift $ http request manager
      (httpsource, finalizer) <- lift $ unwrapResumable (responseBody response)
      httpsource
      lift finalizer
    
    main :: IO ()
    main = do
      manager <- newManager conduitManagerSettings
      runResourceT $ (runquery manager $$ CL.mapM_ (liftIO . print))