Reputation: 893
I want to send a list of Photos to a sink to save them on the disk. Each Photo structure
contains a content
field of type lazy ByteString.
Basically I want to do something like that:
CL.sourceList photos $$ sinkPhotos
I would like to reuse CB.sinkFile
in my sink since, if I understand correctly, it does error management. Here is my attempt so far:
mkFilePath :: Photo -> FilePath
mkFilePath photo = last $ splitOn "/" (url photo)
sinkPhotos :: Sink Photo IO ()
sinkPhotos = do
mphoto <- await
case mphoto of
Nothing -> return ()
Just photo -> do
yield ct =$ CB.sinkFile fp
sinkPhotos
where fp = mkFilePath photo
ct = BL.toStrict $ content photo
It fails with:
src/Screepy/Main.hs:30:23:
No instance for (MonadResource IO)
arising from a use of `CB.sinkFile'
Possible fix: add an instance declaration for (MonadResource IO)
In the second argument of `(=$)', namely `CB.sinkFile fp'
In a stmt of a 'do' block: yield ct =$ CB.sinkFile fp
In the expression:
do { yield ct =$ CB.sinkFile fp;
sinkPhotos }
sinkFile
to work on a structure and not directly a ByteString
?Source
from a ByteString
?Upvotes: 2
Views: 255
Reputation: 2909
I'm not sure I'm following all the questions you ask. I'll just address the type question: the error is only arising because of the signature you supplied. Doesn't this work?
sinkPhotos :: MonadResource m => Sink Photo m ()
sinkPhotos = do
mphoto <- await
case mphoto of
Nothing -> return ()
Just photo -> do
yield ct =$ CB.sinkFile fp
sinkPhotos
where fp = mkFilePath photo
ct = BL.toStrict $ content photo
It is now just a question of finding the right place to use runResourceT
, no? Querying ghci
we see:
>>> let photos = [] :: [Photo]
>>> :t CL.sourceList photos $$ sinkPhotos
CL.sourceList photos $$ sinkPhotos :: MonadResource m => m ()
>>> :t runResourceT $ CL.sourceList photos $$ sinkPhotos
runResourceT $ CL.sourceList photos $$ sinkPhotos
:: (MonadThrow m, MonadBaseControl IO m, Control.Monad.IO.Class.MonadIO m) => m ()
>>> :t runResourceT $ CL.sourceList photos $$ sinkPhotos :: IO ()
runResourceT $ CL.sourceList photos $$ sinkPhotos :: IO ()
So given a list of Photos, you can write
main = runResourceT $ CL.sourceList photos $$ sinkPhotos
Edit: By the way, the type queries in ghci give clearer results if you use the type
sinkPhotos :: Sink Photo (ResourceT IO) ()
though you may have reason to allow for other possibilities.
Upvotes: 3