
Hi Dimitri,
The implementation of `withCSVLifted` looks dodgy to me. If a
downstream consumer terminates early, then the file will never get
closed.
For pipes, the standard solution to resource management is the
pipes-safe[1] package. It handles early termination and IO exceptions
automatically. The example in the docs should fit your use case pretty
well.
[1] https://hackage.haskell.org/package/pipes-safe-2.2.3/docs/Pipes-Safe.html
On Wed, Oct 28, 2015 at 12:25 PM, Dimitri DeFigueiredo
Here's a final pipes example then. I don't think there's a way to fix the problem as Oleg proposed because pipes are monad transformers by design.
The Pipe monad transformer augments the base monad with two operations: - await: gets a result from an upstream pipe - yield: sends a result to a downstream pipe
I have a producer (which is a pipe that can only 'yield') that produces the lines of the .CSV file as Strings and returns () when done:
getFileContentsLifted :: Producer String IO () getFileContentsLifted = withCSVLifted "data.csv" myReadFile where myReadFile :: Handle -> Producer String IO () myReadFile handle = do eof <- lift $ hIsEOF handle unless eof $ do str <- lift $ hGetLine handle yield str myReadFile handle
I then have a simple pipeline that reads each line and prints it twice:
lineDoubler :: Pipe String String IO () lineDoubler = forever $ do s <- await yield s yield s
main = do runEffect $ getFileContentsLifted >-> lineDoubler >-> stdoutLn
The problem as before is that this code does not work with the original version of withCSV:
withCSV :: FilePath -> (Handle -> IO r) -> IO r withCSV path action = do putStrLn "opening file" h <- openFile path ReadMode r <- action h hClose h putStrLn "file closed" return r
only with the lifted (i.e. generalized) one.
withCSVLifted :: MonadIO mIO => FilePath -> (Handle -> mIO r) -> mIO r withCSVLifted path action = do liftIO $ putStrLn "opening file" h <- liftIO $ openFile path ReadMode r <- action h liftIO $ hClose h liftIO $ putStrLn "file closed" return r
And I have the same question: Should I always "generalize" my monadic actions that take callbacks as parameters?
I hope this version is still clear. Thanks for everyone for their input. I thought this was an easier problem than it now appears to be.
Dimitri
PS. Full code is here https://gist.github.com/dimitri-xyz/f1f5bd4c0f7f2bf85379
On 10/26/15 10:47 AM, Kim-Ee Yeoh wrote:
On Mon, Oct 26, 2015 at 11:36 PM, Dimitri DeFigueiredo
wrote: I might have over simplified the problem by using ReaderT in my example. In my original problem this role is played by the Pipes library (and instead of using 'ask', I wanted to 'yield' control to a downstream pipe).
Is there a way you could introduce just enough complexity to allow Oleg another stab?
Also, there's always the fallback of showing your Pipes-based code although that library doesn't enjoy universal familiarity.
-- Kim-Ee
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
-- Chris Wong (https://lambda.xyz) "I fear that Haskell is doomed to succeed." -- Tony Hoare