<div dir="ltr">I'm shooting in the dark, but isn't monad-control's liftBaseWith supposed to address such situations? <a href="https://hackage.haskell.org/package/monad-control-1.0.0.4/docs/Control-Monad-Trans-Control.html#g:3">https://hackage.haskell.org/package/monad-control-1.0.0.4/docs/Control-Monad-Trans-Control.html#g:3</a><div><br></div><div>Robin</div></div><br><div class="gmail_quote"><div dir="ltr">Dimitri DeFigueiredo <<a href="mailto:defigueiredo@ucdavis.edu">defigueiredo@ucdavis.edu</a>> ezt írta (időpont: 2015. okt. 28., Sze, 1:02):<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
  
    
  
  <div bgcolor="#FFFFFF" text="#000000">
    Hi Chris,<br>
    <br>
    You are right. The implementation is totally dodgy! In fact, Pipes
    already has 
    
    <tt>fromHandle</tt> which does this properly. I'm just trying to
    come up with an example of an IO action that takes another IO action
    as a parameter and what to do about using that with a monad
    transformer such as pipes. My focus is what to do when you need to
    use an action such as <tt>withCSV</tt> with a action that is *not*
    in the IO monad.</div><div bgcolor="#FFFFFF" text="#000000"><br>
    <br>
    Dimitri</div><div bgcolor="#FFFFFF" text="#000000"><br>
    <br>
    <div>On 10/27/15 5:52 PM, Chris Wong wrote:<br>
    </div>
    <blockquote type="cite">
      <pre>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] <a href="https://hackage.haskell.org/package/pipes-safe-2.2.3/docs/Pipes-Safe.html" target="_blank">https://hackage.haskell.org/package/pipes-safe-2.2.3/docs/Pipes-Safe.html</a>

On Wed, Oct 28, 2015 at 12:25 PM, Dimitri DeFigueiredo
<a href="mailto:defigueiredo@ucdavis.edu" target="_blank"><defigueiredo@ucdavis.edu></a> wrote:
</pre>
      <blockquote type="cite">
        <pre>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
<a href="https://gist.github.com/dimitri-xyz/f1f5bd4c0f7f2bf85379" target="_blank">https://gist.github.com/dimitri-xyz/f1f5bd4c0f7f2bf85379</a>


On 10/26/15 10:47 AM, Kim-Ee Yeoh wrote:


On Mon, Oct 26, 2015 at 11:36 PM, Dimitri DeFigueiredo
<a href="mailto:defigueiredo@ucdavis.edu" target="_blank"><defigueiredo@ucdavis.edu></a> wrote:
</pre>
        <blockquote type="cite">
          <pre>
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).
</pre>
        </blockquote>
        <pre>

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
<a href="mailto:Haskell-Cafe@haskell.org" target="_blank">Haskell-Cafe@haskell.org</a>
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe" target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe</a>

</pre>
      </blockquote>
      <pre>


</pre>
    </blockquote>
    <br>
  </div>

_______________________________________________<br>
Haskell-Cafe mailing list<br>
<a href="mailto:Haskell-Cafe@haskell.org" target="_blank">Haskell-Cafe@haskell.org</a><br>
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe</a><br>
</blockquote></div>