IO Exceptions through monad transformers stack

Hi, I’m not sure if this is the right list for “base” questions like this, if there are any more specific lists please let me know. 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. The ExceptT transformer make it easy for the lexer and the parser to signal errors. At the end I have a runParser function that compose runExceptT and evalStateT and provides me with a nice Either that contains the parsed result or a value of type ParserError. The ParserError data type is the following: data ParserError = LexingError <fields> | ParsingError <fields> Currently, whenever the parser encounter an error I simply do throwError $ ParsingError something…, for example. 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. How can I do something like that? The root of the problem is that I still have to grasp how the IO exceptions relate to all the other ExceptT, ErrorT, MonadError, types and how all the functions like throwError, catchError, catch, try, and so on work together, in a stack of transformers. Any clarification or pointer would be very appreciated Thank you very much, Nicola

Hi Nicola,
Exceptions are a completely unrelated mechanism to all the monad
transformers. Roughly speaking, it’s a dirty low level hack into Haskell’s
type system. The error-transformer solutions on the other hand are just
libraries, which utilize Haskell’s standard features. So there is no
relation between the two, other then that they approach the same set of
problems.
Concerning your specific problem, the try
http://hackage.haskell.org/package/base-4.7.0.1/docs/Control-Exception-Base....
function is there to help you. In your final solution you may utilize a
function in the spirit of the following:
data ParserError =
IOError IOException | LexingError <fields> | ParsingError <fields>
safeLiftIO :: IO a -> Parser a
safeLiftIO io =
liftIO (try io) >>= either (throwError . IOError) return
Best regards,
Nikita Volkov
2014-10-08 1:25 GMT+04:00 Nicola Gigante
Hi,
I’m not sure if this is the right list for “base” questions like this, if there are any more specific lists please let me know.
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.
The ExceptT transformer make it easy for the lexer and the parser to signal errors. At the end I have a runParser function that compose runExceptT and evalStateT and provides me with a nice Either that contains the parsed result or a value of type ParserError.
The ParserError data type is the following:
data ParserError = LexingError <fields> | ParsingError <fields>
Currently, whenever the parser encounter an error I simply do throwError $ ParsingError something…, for example.
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.
How can I do something like that? The root of the problem is that I still have to grasp how the IO exceptions relate to all the other ExceptT, ErrorT, MonadError, types and how all the functions like throwError, catchError, catch, try, and so on work together, in a stack of transformers.
Any clarification or pointer would be very appreciated
Thank you very much, Nicola _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Il giorno 08/ott/2014, alle ore 00:44, Nikita Volkov
Hi Nicola,
Hi!
Exceptions are a completely unrelated mechanism to all the monad transformers. Roughly speaking, it’s a dirty low level hack into Haskell’s type system. The error-transformer solutions on the other hand are just libraries, which utilize Haskell’s standard features. So there is no relation between the two, other then that they approach the same set of problems.
Concerning your specific problem, the try function is there to help you. In your final solution you may utilize a function in the spirit of the following:
data ParserError = IOError IOException | LexingError <fields> | ParsingError <fields>
safeLiftIO :: IO a -> Parser a safeLiftIO io = liftIO (try io) >>= either (throwError . IOError) return Thank you, this solves my problem. I understand that exceptions are a different mechanism from ExceptT and similar, but I’m still trying to understand how exceptions are supposed to be integrated with the other error handling mechanisms (or vice versa).
Any pointers to better understand what components are involved and best practices for addressing this problems?
Best regards, Nikita Volkov
Thank you, Nicola

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
participants (3)
-
Donn Cave
-
Nicola Gigante
-
Nikita Volkov