Search code examples
haskellmonadsmonad-transformersconduit

MonadResource for reading files with error handling


I'm creating a Conduit that will read binary files. Stuff can go wrong, so I need a monad with some error handling; for now Maybe is good enough.

I'd like to use sourceFile, which requires that the conduit monad be a MonadResource, and this is the crux of the problem.

I see from the docs that e.g. MaybeT m has an instance, but it requires m to already be a MonadResource; in fact this is true of all the instances. With my limited understanding this kinda sounds like a chicken-and-egg thing, requiring that I write a MonadResource instance by hand no matter what?

I assume that to read files, my monad has to contain IO. So does all this mean that I have to write a MonadResource instance for MaybeT IO? If so, any pointers on how to do that?


Solution

  • A simple way is to use tryC for example:

    module Main (main) where
    
    import           Conduit
    import           Control.Exception (SomeException)
    import qualified Control.Monad.Trans.Resource as R
    import           Data.Monoid ((<>))
    import           System.Environment (getArgs)
    
    main :: IO ()
    main = do
      [fname] <- getArgs
      r <- R.runResourceT . runConduit . tryC $ sourceFile fname .| await >>= pure
      case r of
        Left e -> putStrLn $ "Failed to read file content with " <> show (e :: SomeException)
        Right r' -> putStrLn $ "File content: " <> show r'
    

    Then you get:

    [nix-shell:/tmp]$ ghc -Wall M.hs && ./M /tmp/doesnt_exist
    [1 of 1] Compiling Main             ( M.hs, M.o )
    Linking M ...
    Failed to read file content with /tmp/doesnt_exist: openBinaryFile: does not exist (No such file or directory)
    
    [nix-shell:/tmp]$ ghc -Wall M.hs && ./M /tmp/hello-file
    File content: Just "Hello world!