[Haskell-cafe] PROPOSAL: Web application interface

Michael Snoyman michael at snoyman.com
Sun Jan 24 01:12:58 EST 2010


On Sun, Jan 24, 2010 at 2:38 AM, Nicolas Pouillard <
nicolas.pouillard at gmail.com> wrote:

> On Sat, 23 Jan 2010 21:31:47 +0200, Michael Snoyman <michael at snoyman.com>
> wrote:
> > Just as an update, I've made the following changes to my WAI git repo (
> > http://github.com/snoyberg/wai):
> >
> > * I removed the RequestBody(Class) bits, and replaced them with "IO
> (Maybe
> > ByteString)". This is a good example of tradeoffs versus the enumerator
> > approach (see below).
>
> * Are you sure that a strict bytestring is fine here? Can the request body
>  be used to upload large data? If so why not use a lazy one, not (only)
>  for its laziness but for being a list of chunks that fits well into memory
>  caches...
>
> Sorry, this is where I should have put in some documentation. The IO (Maybe
ByteString) returns chunks of strict bytestring until it encounters the end
of the body. If we were to use a lazy bytestring, we would either need lazy
I/O or to read everything into memory (which is what we're trying to avoid).

The handler has the prerogative to determine chunk size.

* I would call ResponseBody a ResponseReceiver given its use and the sigs
>  of the methods.
>
> * Why not use sendLazyByteString in sendFile as a default method, this
>  will fix your "TODO" since I believe the chunk size would be a good one.
>
> * Maybe a ResponseReceiver Handle instance could be provided. Since it
>  requires no data type definition and would make an orphan instance
> elsewhere.
>  Maybe a one for sockets would make sense as well.
>
> Sorry, I added a few more patches since sending this e-mail. I did away
with ResponseBody as well, and replaced it with Either FilePath ((ByteString
-> IO ()) -> IO ()). This is *very* close to the Hyena version in my
opinion, with three differences (I think I've written these elsewhere, so
sorry if I'm repeating myself).

1) It provides the option of providing optimized file sending, as per the
Happstack sendfile system call. I was concerned at first that we might wish
to provide sending multiple files, but I think people will prefer the
simplicity that comes without having a typeclass. I'm completely open to
revisiting this issue, as I have no strong feelings.
2) There is no "accumulating parameter" as there is with Hyena. In the
general case, I don't think it's necesary, and when it is, we can use MVars.
3) There is no built in way to force early termination. I think this is a
better approach, since early termination would be an exceptional situation.
Forcing the application to check a return value each time would be overhead
that would rarely be used, and we can achieve the same effect with an
exception.

Sorry for not sending this update earlier, but I only finished at about 2:30
last night. I found it difficult to write coherently.

> * This might just be bikeshedding, but renamed RequestMethod to Method to
> > make names slightly shorter and more consistent.
>
> Good for me
>
> > * I implemented Mark's suggestions of adding support for arbitrary
> request
> > methods and information on HTTP version.
>
> Nice
>
> > I've been having some off-list discussions about WAI, and have a few
> issues
> > to bring up. The first is relatively simple: what do we do about
> consuming
> > the entire request body? Do we leave that as a task to the application,
> or
> > should the server ensure that the entire request body is consumed?
>
> Good question, is there something in the HTTP spec about this. I don't
> think
> so, and I think it would make sense to give up early if you consider the
> input as garbage.
>
> What do you mean by this? That we don't need to consume that input at all,
or that the server should be held responsible for "/dev/null"ing  the data?

> Next, I have made the ResponseBodyClass typeclass specifically with the
> goal
> > of allowing optimizations for lazy bytestrings and sending files. The
> former
> > seems far-fetched; the latter provides the ability to use a sendfile
> system
> > call instead of copying the file data into memory. However, in the
> presence
> > of gzip encoding, how useful is this optimization?
>
> It is useful anyway.
>
> > Finally, there is a lot of discussion going on right now about
> enumerators.
> > The question is whether the WAI protocol should use them. There are two
> > places where they could replace the current offering: request body and
> > response body.
> >
> > In my opinion, there is no major difference between the Hyena definition
> of
> > an enumerator and the current response body sendByteString method. The
> > former provides two extra features: there's an accumulating parameter
> passed
> > around, and a method for indicating early termination. However, the
> > accumulating parameter seems unnecesary to me in general, and when needed
> we
> > can accomplish the same result with MVars. Early termination seems like
> > something that would be unusual in the response context, and could be
> > handled with exceptions.
>
> IORefs could be sufficient (instead of MVars) but this seems a bit ugly
> compared to the accumulator. In the other hand sometimes you don't need
> the accumulator and so just pass a dump unit. If we live in IO yes
> exceptions
> could do that. However the point of the Either type is to remind you that
> you have two cases to handle.
>
>
> For the request body, there is a significant difference. However, I think
> > that the current approach (called imperative elsewhere) is more in line
> with
> > how most people would expect to program. At the same time, I believe
> there
> > is no performance issue going either way, and am open to community input.
>
> Why an imperative approach would be more in line when using a purely
> functional language?
>
> Regards,
>
> --
> Nicolas Pouillard
> http://nicolaspouillard.fr
>

Because I don't think it really *is* an imperative approach. For that
matter, enumerators are frankly also an "imperative approach." It's frankly
a silly distinction IMO. The question is whether this is a *good* approach.
I think passing in an output function fits very nicely with Haskell.

The question to me lies more on the request side than the response side.
Basically, should the application provide a caller or a callee for reading
the request body? Most of the time, the latter is simpler to write I
believe.

Ha! I finally found the article I'd read a while ago demonstrating this
point in C. You can obviously disagree with the sentiment there, but I've
found the point to be true in Haskell as well:
http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html

Michael
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/haskell-cafe/attachments/20100124/48372193/attachment.html


More information about the Haskell-Cafe mailing list