1
votes

I'm trying to get a simple conduit example under my belt, but I'm failing at the type-checking phase. In this example, I'm seeing ResourceT being used as the conduit "executor", if that makes sense. I also know that, for some time, ResourceT has been factored-out into it's own package. However, I still can't get a simple example working. Here is my code, which was inspired by this article:

import qualified Data.Conduit as C
import qualified Data.Conduit.Binary as CB
import qualified Data.Conduit.List as CL
import Control.Monad.Trans.Resource (runResourceT)
import Data.ByteString.Char8 (unpack)
import Data.ByteString (ByteString (..))
import System.IO

printFile file = runResourceT $ CB.sourceFile file C.$$ print'
  where
    print' :: C.Sink ByteString IO ()
    print' = CL.mapM_ $ putStrLn · unpack

And here is the error I'm getting (my package is called "conduit-playground":

Preprocessing library conduit-playground-0.0.0...
[2 of 2] Compiling Playground       ( src/Playground.hs, dist/dist-sandbox-13da96d6/build/Playground.o )

src/Playground.hs:12:57:
    Couldn't match type ‘IO’
                  with ‘Control.Monad.Trans.Resource.Internal.ResourceT m’
    Expected type: C.Sink
                     ByteString (Control.Monad.Trans.Resource.Internal.ResourceT m) ()
      Actual type: C.Sink ByteString IO ()
    Relevant bindings include
      printFile :: FilePath -> m () (bound at src/Playground.hs:12:1)
    In the second argument of ‘(C.$$)’, namely ‘print'’
    In the second argument of ‘($)’, namely
      ‘CB.sourceFile file C.$$ print'’
cabal: Error: some packages failed to install:
conduit-playground-0.0.0 failed during the building phase. The exception was:
ExitFailure 1

If I leave out runResourceT, I'm still getting a typeclass error for there not being an instance of MonadResource for IO. How should I tackle this problem?

1
I'm not familiar with ResourceT, but it appears you're trying to force a type of print' that doesn't match the transformer stack. What happens if you remove the explicit signature? - Sarah
It assumes print' to be a Conduit instead of a Sink, causing even more type errors :/ - Athan Clark
I think, really, this comes down to not having an instance of MonadResource for IO. For instance, CL.mapM_ putStrLn :: MonadResource m => Sink String m () doesn't work; there's no instance (probably for a good reason). - Athan Clark

1 Answers

4
votes

The problem is that your print' has an incompatible type

print' :: C.Sink ByteString IO ()

You can't have IO as the base monad of a conduit if you are using ResourceT (which you need in this case for reading the file). The simplest monad that lets you both print and handle file resources is ResourceT IO i.e. the IO monad transformed with the ResourceT transformer.

print' :: C.Sink ByteString (ResourceT IO) ()
print' = CL.mapM_ $ liftIO . putStrLn · unpack

The liftIO is needed to "lift" the expression of type IO () into ResourceT IO (). You could also use the more generic signature

print' :: MonadIO m => C.Sink ByteString m ()

to make a utility function that works with any conduit that is able to perform IO.

MonadIO and liftIO can be imported from the module Control.Monad.IO.Class.