
quoth Nicola Gigante
I'm writing a parser and it works in a stack of monad transformers defined in this way:
type Parser = StateT ParserState (ExceptT ParserError IO)
The base monad is IO because the parse could have the need to load other files included by the file that's being parsed. ... What I would like to do is to report in this way also the possible IO errors that can occur, by adding a IOError constructor to the datatype above and having runParser return such a value if any operation on the underlying IO monad throws an exception.
I don't have a real answer for your question, but I can tell you that I don't have this problem in my parser, even though it depends on I/O in a similar way. The need for more input data is just one of the errors or exceptions that it returns. So to use this parser, the application gets data by whatever means, such as input from a file, and applies the parser to it. The parser returns a parse result, a parse error, or an incomplete specifying the required input. The application gets that input and reapplies the parser ... etc. I have a sort of degenerate case of the problem, inasmuch as the stuff I'm parsing (IMAP4) is a one-liner expression that may be followed by a counted large data block - so the parsing per se is a very trivial computation and can simply be repeated on each iteration - and since the counted large data blocks aren't for the parser, I can copy them straight into a file, not into memory. Your application would likely be quite different. But in any case, in my opinion it's worth a little extra trouble to separate I/O from parsing, because it makes the parser more adaptable to circumstances the application might be equipped to deal with. Maybe for example, certain input needs a password? Who knows. And then it can do what it wants with IOError. Donn