[Haskell-cafe] Using Wai with Conduit and ResourceT (was Re: (New to Conduits) mixing lazy lists and Conduits?)

Michael Snoyman michael at snoyman.com
Fri Jun 29 09:12:28 UTC 2018


I'd have to see a complete repro to know why the program in question
doesn't stream. But I _can_ explain how best to do something like this.

To frame this: why is something like ResourceT needed here? The issue is we
want to ensure exception safety around the open file handle, and guarantee
that the handle is closed regardless of any exceptions being thrown.
ResourceT solves this problem by letting you register cleanup actions. This
allows for solving some really complicated dynamic allocation problems, but
for most cases it's overkill. Instead, a simple use of the bracket pattern
is sufficient. You can do that with `withSourceFile`:

```
#!/usr/bin/env stack
-- stack --resolver lts-11.10 script
import Network.Wai
import Network.Wai.Handler.Warp
import Network.Wai.Conduit
import Network.HTTP.Types
import Conduit
import Data.ByteString.Builder (byteString)

main :: IO ()
main = run 3000 app

app :: Application
app _req respond =
  withSourceFile "Main.hs" $ \src ->
  respond $ responseSource status200 []
          $ src .| mapC (Chunk . byteString)
```

You can also do this by sticking with ResourceT, which requires jumping
through some hoops with monad transformers to ensure the original ResourceT
context is used. I don't recommend this approach unless you really need it:
it's complicated, and slightly slower than the above. But in case you're
curious:

```
#!/usr/bin/env stack
-- stack --resolver lts-11.10 script
import Network.Wai
import Network.Wai.Handler.Warp
import Network.Wai.Conduit
import Network.HTTP.Types
import Conduit
import Data.ByteString.Builder (byteString)
import Control.Monad.Trans.Resource

main :: IO ()
main = run 3000 app

app :: Application
app _req respond =
  runResourceT $ withInternalState $ \is ->
  respond $ responseSource status200 [] $
  transPipe (`runInternalState` is) (sourceFile "Main.hs") .|
  mapC (Chunk . byteString)
```

On Fri, Jun 29, 2018 at 12:05 AM Jon Fairbairn <jon.fairbairn at cl.cam.ac.uk>
wrote:

> Michael Snoyman <michael at snoyman.com> writes:
>
> > You can use Data.Conduit.Lazy for this.
>
> Thanks. Not as straightforward as I had hoped, but I can see
> why.
>
> On a different note, still attempting to learn, I am trying to
> use Network.Wai.Conduit with a conduit that has effects (ie
> involves sourceFile), and so lives in (ResourceT IO). eg
>
> example:: ConduitT i (Flush Builder) (ResourceT IO) ()
>
> Now, responseSource expects IO, not ResourceT IO, so I don’t
> think I can use that, so I wrote this:
>
>
> > responseSourceRes status headers res_conduit
> >   = responseStream status200 headers
> >     (\send flush -> runConduitRes $ res_conduit
> >                     .| mapM_ (\e->lift $
> >                                   case e of
> >                                     Chunk c -> send c
> >                                     Flush -> flush ))
>
> which runs, but (rather to my surprise) doesn’t produce output
> (not even headers) until all the effects have completed. That
> gives rise to two questions:
>
> Why does that not stream output?
> What should I do instead?
>
> --
> Jón Fairbairn                                 Jon.Fairbairn at cl.cam.ac.uk
>
> _______________________________________________
> Haskell-Cafe mailing list
> To (un)subscribe, modify options or view archives go to:
> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
> Only members subscribed via the mailman list are allowed to post.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/haskell-cafe/attachments/20180629/a7ee33af/attachment.html>


More information about the Haskell-Cafe mailing list