
On Sun, Oct 9, 2011 at 3:57 PM, Arnaud Bailly
Hello, I am trying to move a web application I wrote that initially used raw sockets for doing HTTP by "hand" to a more sensible Wai-based framework, and I am running into a design issue. I initially thought it would be a good idea to be able to support various I/O methods so I defined a layer called CommandIO (this is a game application) to insulate business-level code from the detials of reading commands and outputting results to the outside world. Here is the signature for this simple monad:
class (Monad io) => CommandIO io where readCommand :: io Command writeResult :: CommandResult -> io ()
then I have an interpret function which looks like:
interpret :: (CommandIO io, Map t) => Commands t io CommandResult
What is the 'Commands' type? What is the 'Map' class?
which reads a command from the io, execute it producing a CommandResult, then request output from the io using writeResult.
This works nicely using Reader-based data containing a Handle, both for HTTP-based and console-based I/O.
But when I try to move to WAI, I am a little bit stuck. The core of Wai is an application which rougly has type Request -> Response and this basically breaks my (simple) model of read commands / write results. I suspect there might be a way to stick to my current model using enumerators/streams for I/O and a custom type for encapsulating the underlying state of the HTTP exchange. I also thought about reversing the dependency : ensuring that interpret does not need to know about low-level I/O and only dealing with Command/CommandResult, with I/O being handled by wrappers.
Your type class as written doesn't appear to work too well for HTTP - what would happen if I called 'readCommand' twice? Or called 'writeResult' before receiving a command? HTTP is, at its core, a server which responds to a single request with a single response. The WAI type reflects in a way that makes it hard to do wrong. This doesn't work because the API you want to provide makes it possible to do wrong. Your class might be able to fit into WAI if each back-end provided a single function:
execute :: (Command -> io CommandResult) -> io ()
This should make sense for console, Handle-based and HTTP. Am I on the right track? Antoine