Exception handling when using STUArray

Dear list, I'm using STUArray in some of my time critical number crunching code. I would like to know some way to catch the exceptions raised in the ST monad, ie. ArrayException. Looking through the Control.Exception module, I understand that those functions can only be used within IO monad. So my question is, what the best way to do exception handling in STUArray is. Plus, it would be helpful if I can also raise an exception in ST monad, but how? Thanks, Xiao-Yong -- c/* __o/* <\ * (__ */\ <

On Sat, Mar 8, 2008 at 12:29 AM, Xiao-Yong Jin
I'm using STUArray in some of my time critical number crunching code. I would like to know some way to catch the exceptions raised in the ST monad, ie. ArrayException.
I am also using STUArray from some time-critical code; however, I don't deal with ArrayException, or any exceptions for that matter. What besides an out-of-bounds read or write might throw an ArrayException? If it is out-of-bounds reading or writing, surely that indicates a bug in your program that you'd rather fix than catch the exception, no? Also, if you're using GHC, note that the ArrayException documentation says: "(NOTE: GHC currently does not throw ArrayExceptions)."
Looking through the Control.Exception module, I understand that those functions can only be used within IO monad. So
This isn't quite true, if I understand what you mean. The function throw can be used from any code (http://haskell.org/ghc/docs/latest/html/libraries/base/Control-Exception.htm...). But you must catch exceptions in the IO monad.
my question is, what the best way to do exception handling in STUArray is. Plus, it would be helpful if I can also raise an exception in ST monad, but how?
I think what I noted in the last paragraph solves this problem. -- Denis

"Denis Bueno"
On Sat, Mar 8, 2008 at 12:29 AM, Xiao-Yong Jin
wrote: I'm using STUArray in some of my time critical number crunching code. I would like to know some way to catch the exceptions raised in the ST monad, ie. ArrayException.
I am also using STUArray from some time-critical code; however, I don't deal with ArrayException, or any exceptions for that matter. What besides an out-of-bounds read or write might throw an ArrayException? If it is out-of-bounds reading or writing, surely that indicates a bug in your program that you'd rather fix than catch the exception, no?
In my case, because I choose a index of the array according to certain value in the array, if there is NaN or Infinity in the array, the code breaks. So I guess, to fix the code, I would probably use isNaN/isInfinite and throw an exception when that happens.
Also, if you're using GHC, note that the ArrayException documentation says:
"(NOTE: GHC currently does not throw ArrayExceptions)."
Looking through the Control.Exception module, I understand that those functions can only be used within IO monad. So
This isn't quite true, if I understand what you mean. The function throw can be used from any code (http://haskell.org/ghc/docs/latest/html/libraries/base/Control-Exception.htm...). But you must catch exceptions in the IO monad.
Thanks for the clarification. I didn't read the documentation carefully. I'll experiment on `throw', right now. Xiao-Yong -- c/* __o/* <\ * (__ */\ <

On Sat, Mar 8, 2008 at 9:54 AM, Xiao-Yong Jin
ArrayException? If it is out-of-bounds reading or writing, surely that indicates a bug in your program that you'd rather fix than catch the exception, no?
In my case, because I choose a index of the array according to certain value in the array, if there is NaN or Infinity in the array, the code breaks. So I guess, to fix the code, I would probably use isNaN/isInfinite and throw an exception when that happens.
I see. In that case, you should check out the ErrorT monad transformer (http://haskell.org/ghc/docs/latest/html/libraries/mtl/Control-Monad-Error.ht...); it lets you throw an error conveniently using Either. It may or may not fit your needs, but at least it's worth knowing about. And in case you don't understand monad transformers: if you understand the State monad, the following post is a fairly nice, minimal explanation of monad transformers: http://sigfpe.blogspot.com/2006/05/grok-haskell-monad-transformers.html -- Denis

"Denis Bueno"
On Sat, Mar 8, 2008 at 9:54 AM, Xiao-Yong Jin
wrote: ArrayException? If it is out-of-bounds reading or writing, surely that indicates a bug in your program that you'd rather fix than catch the exception, no?
In my case, because I choose a index of the array according to certain value in the array, if there is NaN or Infinity in the array, the code breaks. So I guess, to fix the code, I would probably use isNaN/isInfinite and throw an exception when that happens.
I see. In that case, you should check out the ErrorT monad transformer (http://haskell.org/ghc/docs/latest/html/libraries/mtl/Control-Monad-Error.ht...); it lets you throw an error conveniently using Either. It may or may not fit your needs, but at least it's worth knowing about.
And in case you don't understand monad transformers: if you understand the State monad, the following post is a fairly nice, minimal explanation of monad transformers: http://sigfpe.blogspot.com/2006/05/grok-haskell-monad-transformers.html
Thanks. I do need to learn some monad transformers. And this post is indeed very helpful. Xiao-Yong -- c/* __o/* <\ * (__ */\ <

On Sat, 8 Mar 2008, Denis Bueno wrote:
On Sat, Mar 8, 2008 at 12:29 AM, Xiao-Yong Jin
wrote: I'm using STUArray in some of my time critical number crunching code. I would like to know some way to catch the exceptions raised in the ST monad, ie. ArrayException.
I am also using STUArray from some time-critical code; however, I don't deal with ArrayException, or any exceptions for that matter. What besides an out-of-bounds read or write might throw an ArrayException? If it is out-of-bounds reading or writing, surely that indicates a bug in your program that you'd rather fix than catch the exception, no?
Another instance of mixing up exceptions and errors in the Haskell libraries. http://www.haskell.org/haskellwiki/Error http://www.haskell.org/haskellwiki/Exception
Also, if you're using GHC, note that the ArrayException documentation says:
"(NOTE: GHC currently does not throw ArrayExceptions)."
Looking through the Control.Exception module, I understand that those functions can only be used within IO monad. So
This isn't quite true, if I understand what you mean. The function throw can be used from any code (http://haskell.org/ghc/docs/latest/html/libraries/base/Control-Exception.htm...). But you must catch exceptions in the IO monad.
my question is, what the best way to do exception handling in STUArray is. Plus, it would be helpful if I can also raise an exception in ST monad, but how?
I think what I noted in the last paragraph solves this problem.

On Mar 8, 2008, at 12:33 PM, Henning Thielemann wrote:
On Sat, 8 Mar 2008, Denis Bueno wrote: ...
I am also using STUArray from some time-critical code; however, I don't deal with ArrayException, or any exceptions for that matter. What besides an out-of-bounds read or write might throw an ArrayException? If it is out-of-bounds reading or writing, surely that indicates a bug in your program that you'd rather fix than catch the exception, no?
Another instance of mixing up exceptions and errors in the Haskell libraries. http://www.haskell.org/haskellwiki/Error http://www.haskell.org/haskellwiki/Exception
This seems to me one of the disappointments of Haskell - not just a detail that was handled in an awkward way, but a fundamental flaw. I'm not talking about ArrayException, whatever that is, but the notion that errors encountered in functional code mustn't be handled as exceptions. If your input file is not where you expected it and openFile fails, or it was incompletely written out and your input processing fails in a 'head' or pattern match, the difference is not very important to me. I want to be able to call your code and manage the risk that it's going to kill my server. You may feel that it's your job to write more robust functional code that can't run into these errors, but C programmers can be found who insist that it's the programmer's job to manage heap memory and it isn't all that hard. A programming language that fails to make it easier to write more robust code, is not moving us forward. I rejoice that Haskell isn't as miserable as C, but with respect to exceptions and errors, it's behind for example Python. Languages that can, use exceptions like IndexError. Evidently, Haskell fundamentally can't. That's too bad. Donn Cave, donn@avvanta.com

donn:
On Mar 8, 2008, at 12:33 PM, Henning Thielemann wrote:
On Sat, 8 Mar 2008, Denis Bueno wrote: ...
I am also using STUArray from some time-critical code; however, I don't deal with ArrayException, or any exceptions for that matter. What besides an out-of-bounds read or write might throw an ArrayException? If it is out-of-bounds reading or writing, surely that indicates a bug in your program that you'd rather fix than catch the exception, no?
Another instance of mixing up exceptions and errors in the Haskell libraries. http://www.haskell.org/haskellwiki/Error http://www.haskell.org/haskellwiki/Exception
This seems to me one of the disappointments of Haskell - not just a detail that was handled in an awkward way, but a fundamental flaw. I'm not talking about ArrayException, whatever that is, but the notion that errors encountered in functional code mustn't be handled as exceptions.
If your input file is not where you expected it and openFile fails, or it was incompletely written out and your input processing fails in a 'head' or pattern match, the difference is not very important to me. I want to be able to call your code and manage the risk that it's going to kill my server. You may feel that it's your job to write more robust functional code that can't run into these errors, but C programmers can be found who insist that it's the programmer's job to manage heap memory and it isn't all that hard. A programming language that fails to make it easier to write more robust code, is not moving us forward. I rejoice that Haskell isn't as miserable as C, but with respect to exceptions and errors, it's behind for example Python. Languages that can, use exceptions like IndexError. Evidently, Haskell fundamentally can't. That's too bad.
I don't understand this complaint -- you can handle all these with Control.Exception. xmonad catches all these things for example, in user code, to prevent poorly written modules throwing a pattern match exception, or calling 'error' and making the window manager unstable. Handling exceptions generated from pure code is just another part of making systems more robust -- and of course you can do it in Haskell. -- Don

On Mar 9, 2008, at 1:54 , Don Stewart wrote:
donn:
This seems to me one of the disappointments of Haskell - not just a detail that was handled in an awkward way, but a fundamental flaw. I'm not talking about ArrayException, whatever that is, but the notion that errors encountered in functional code mustn't be handled as exceptions.
I don't understand this complaint -- you can handle all these with Control.Exception.
xmonad catches all these things for example, in user code, to prevent poorly written modules throwing a pattern match exception, or calling 'error' and making the window manager unstable.
Handling exceptions generated from pure code is just another part of making systems more robust -- and of course you can do it in Haskell.
I'm unsure what the complaint here is as well. That exceptions must be handled in IO: an exception is, by definition, an unexpected condition. These simply can not exist in the deterministic paradigm of pure code, therefore you must be in IO (home of all things nondeterministic) to catch them. That it is preferred to handle known cases without using exceptions: staying completely pure is cleaner, and gives you more control over what goes on. And enables swapping in different mechanisms to react to failure; note how using the Monad instances of Maybe, Either, or [] allows you to decide how to handle failure (and [] allows you to support multiple possible results as well, while Maybe and Either will stop processing at the first. Basically, the Haskell way is to make as much as possible pure and minimize the intrusion of IO. Exceptions necessarily complicate this, so are when possible avoided in favor of pure alternatives, which are considered better style --- but they are available in IO if needed. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

On the other hand, there are lots of issues that can be worked on here, foax! While tools like the one is ndm is working on seem to offer a way forward, at the moment, there's no standard way that I know of besides "inspection" to determine if code might throw an exception, and this is particularly the case with the dreaded lazy IO of prelude functions. And as per a recent discussion on libraries, it's still a royal pain to catch IO exceptions in monads with an IO base. And then, of course, there's the issue of exceptions in ST or STM which have similar issues. All this could be solved if we standardized on some version of gcatch over MonadBase or a variant. The machinery just hasn't been fully written yet, although Control.Monad.Error is, sort of, a start. And of course, using head or any other partial functions in production code without a great deal of care is a really *bad idea*, but the prelude sort of locks us into having them at least. Tutorials and examples might be better served emphasizing why these things are dangerous, and common idioms for avoiding them... Regards, sclv On Mar 9, 2008, at 3:09 AM, Brandon S. Allbery KF8NH wrote:
On Mar 9, 2008, at 1:54 , Don Stewart wrote:
donn:
This seems to me one of the disappointments of Haskell - not just a detail that was handled in an awkward way, but a fundamental flaw. I'm not talking about ArrayException, whatever that is, but the notion that errors encountered in functional code mustn't be handled as exceptions.
I don't understand this complaint -- you can handle all these with Control.Exception.
xmonad catches all these things for example, in user code, to prevent poorly written modules throwing a pattern match exception, or calling 'error' and making the window manager unstable.
Handling exceptions generated from pure code is just another part of making systems more robust -- and of course you can do it in Haskell.
I'm unsure what the complaint here is as well.
That exceptions must be handled in IO: an exception is, by definition, an unexpected condition. These simply can not exist in the deterministic paradigm of pure code, therefore you must be in IO (home of all things nondeterministic) to catch them.
That it is preferred to handle known cases without using exceptions: staying completely pure is cleaner, and gives you more control over what goes on. And enables swapping in different mechanisms to react to failure; note how using the Monad instances of Maybe, Either, or [] allows you to decide how to handle failure (and [] allows you to support multiple possible results as well, while Maybe and Either will stop processing at the first.
Basically, the Haskell way is to make as much as possible pure and minimize the intrusion of IO. Exceptions necessarily complicate this, so are when possible avoided in favor of pure alternatives, which are considered better style --- but they are available in IO if needed.
-- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Mar 8, 2008, at 10:54 PM, Don Stewart wrote: [... replying to my poorly informed rant about exceptions ... ]
I don't understand this complaint -- you can handle all these with Control.Exception.
xmonad catches all these things for example, in user code, to prevent poorly written modules throwing a pattern match exception, or calling 'error' and making the window manager unstable.
Handling exceptions generated from pure code is just another part of making systems more robust -- and of course you can do it in Haskell.
OK, I tried this out and found that it does work, and I thought to myself, `no more posting rants to haskell-cafe after late nights out with too much wine!' But then I changed my test error from a pattern match, to a `head', and that gets past my exception handler: module Main (main) where import System (getArgs) ax = getArgs >>= print . head px = catch ax (\ e -> putStrLn ("caught this one: " ++ show e)) main = px Is there a way to catch it, that I'm missing? What is the essential difference between these errors? If we only have to bring IO out to the locations where we want to handle errors, then that's not so bad as I thought. Incidentally, I also reviewed again the two Wiki pages previously recommended in this thread, for Error and Exception, and I really don't follow them. I know the author is well versed in Haskell and I'm sure he's trying to convey some valuable notion about the difference between errors and exceptions, but it doesn't come across and I don't think it's just me. Donn Cave, donn@avvanta.com

donn:
On Mar 8, 2008, at 10:54 PM, Don Stewart wrote:
[... replying to my poorly informed rant about exceptions ... ]
I don't understand this complaint -- you can handle all these with Control.Exception.
xmonad catches all these things for example, in user code, to prevent poorly written modules throwing a pattern match exception, or calling 'error' and making the window manager unstable.
Handling exceptions generated from pure code is just another part of making systems more robust -- and of course you can do it in Haskell.
OK, I tried this out and found that it does work, and I thought to myself, `no more posting rants to haskell-cafe after late nights out with too much wine!' But then I changed my test error from a pattern match, to a `head', and that gets past my exception handler:
module Main (main) where import System (getArgs)
ax = getArgs >>= print . head
px = catch ax (\ e -> putStrLn ("caught this one: " ++ show e))
main = px
Is there a way to catch it, that I'm missing? What is the essential difference between these errors?
That's the difference between Prelude.catch and Control.Exception.catch. You almost always want Control.Exception.catch. Prelude.catch: $ runhaskell A.hs "A.hs: Prelude.head: empty list Control.Exception.catch $ runhaskell A.hs "caught this one: Prelude.head: empty list As the docs for Control.Exception say: -- Note that 'catch' catches all types of exceptions, and is generally -- used for \"cleaning up\" before passing on the exception using -- 'throwIO'. -- Also note that the "Prelude" also exports a function called -- 'Prelude.catch' with a similar type to 'Control.Exception.catch', -- except that the "Prelude" version only catches the IO and user -- families of exceptions (as required by Haskell 98). -- -- We recommend either hiding the "Prelude" version of 'Prelude.catch' -- when importing "Control.Exception" There's a number of other useful exception handlers: handle finally bracket All useful, all have their place. -- Don

On Sat, 8 Mar 2008, Donn Cave wrote:
On Mar 8, 2008, at 12:33 PM, Henning Thielemann wrote:
On Sat, 8 Mar 2008, Denis Bueno wrote: ...
I am also using STUArray from some time-critical code; however, I don't deal with ArrayException, or any exceptions for that matter. What besides an out-of-bounds read or write might throw an ArrayException? If it is out-of-bounds reading or writing, surely that indicates a bug in your program that you'd rather fix than catch the exception, no?
Another instance of mixing up exceptions and errors in the Haskell libraries. http://www.haskell.org/haskellwiki/Error http://www.haskell.org/haskellwiki/Exception
This seems to me one of the disappointments of Haskell - not just a detail that was handled in an awkward way, but a fundamental flaw. I'm not talking about ArrayException, whatever that is, but the notion that errors encountered in functional code mustn't be handled as exceptions.
Errors are programming errors and must be fixed as Denis explained. Thus there is no need for a complex system of handling these situations at run-time. The program error might be unexpected but it isn't the fault of the user or of the context the program runs in but of the fault of the programmer. The program may report "bug detected, send an e-mail to the author" but eventually it should quit (at least the buggy thread) before worse things happen. This is possible in Haskell but should not be mixed up with handling of exceptions like "non-existing file".
I rejoice that Haskell isn't as miserable as C, but with respect to exceptions and errors, it's behind for example Python. Languages that can, use exceptions like IndexError.
How precisely would you handle IndexError if it would be an exception and not just an error?

On Mar 9, 2008, at 1:07 PM, Henning Thielemann wrote:
Errors are programming errors and must be fixed as Denis explained. Thus there is no need for a complex system of handling these situations at run-time. The program error might be unexpected but it isn't the fault of the user or of the context the program runs in but of the fault of the programmer. The program may report "bug detected, send an e-mail to the author" but eventually it should quit (at least the buggy thread) before worse things happen. This is possible in Haskell but should not be mixed up with handling of exceptions like "non-existing file".
I am not sure I see the difference in principle that you see. An exception is, for me, any state that isn't properly accounted for in its immediate context. openFile could return 'Maybe Handle', but it doesn't, so the context demands a Handle or an exception. 'head' demands data with at least one element, or an exception. Now, we do have the means to validate the latter assumption, and not the former - I can check that a file exists, but can't usually guarantee that it will exist at the instant I try to open it. Is that where you find the distinction?
How precisely would you handle IndexError if it would be an exception and not just an error?
Well, to take a hypothetical example ... I have never looked into JPEG image decoding, but suppose that it's moderately complicated and further that it involves array indexing at some point, and I have written a web browser that needs to decode and display images. Maybe one in ten thousand JPEG images will be invalid for some reason, in a way that leads to an index beyond the bounds of its sequence. Because we have the data at hand, we can certainly validate indices vs. bounds and avoid an index error. Then we need an Either or Maybe or some such thing to express that, and some way to propagate the issue up to the top of the image renderer. Whereupon it will refuse to render the image and will put its broken image icon in its place. Or, I can just write the JPEG decoding algorithm, and catch index errors at that same place, and refuse to render the image etc.. To me, this doesn't pose any moral problem, and the code is bound to be clearer and more expressive of its core intention, than if it were burdened with layer upon layer of Rights and Justs to defend against a problem that has no real solution and may never even occur. If the image I'm supposed to decode isn't actually there when I try to open the file, then I'll display the very same icon in its place. We want the same thing to happen, whatever the problem with the image: display the broken image icon, and go on to render the rest of the page. Now if we want to be philosophical about it, all of these problems don't have to be with the image - maybe it's a variant JPEG type that I didn't know about, or even just my coding error that happened to escape testing. The web browser should carry on regardless, however, so the implementation shouldn't be sensitive to these philosophical distinctions. Donn Cave, donn@avvanta.com

On Mon, Mar 10, 2008 at 7:37 AM, Donn Cave
On Mar 9, 2008, at 1:07 PM, Henning Thielemann wrote:
Errors are programming errors and must be fixed as Denis explained. Thus there is no need for a complex system of handling these situations at run-time. The program error might be unexpected but it isn't the fault of the user or of the context the program runs in but of the fault of the programmer. The program may report "bug detected, send an e-mail to the author" but eventually it should quit (at least the buggy thread) before worse things happen. This is possible in Haskell but should not be mixed up with handling of exceptions like "non-existing file".
I am not sure I see the difference in principle that you see.
An exception is, for me, any state that isn't properly accounted for in its immediate context. openFile could return 'Maybe Handle', but it doesn't, so the context demands a Handle or an exception. 'head' demands data with at least one element, or an exception. Now, we do have the means to validate the latter assumption, and not the former - I can check that a file exists, but can't usually guarantee that it will exist at the instant I try to open it. Is that where you find the distinction?
Playing devil's advocate, I can see a difference in that openFile depends on the environment. But that's not a good reason. Another difference is that when you're in the IO monad (or alternatively any MonadError), failing (throwing an exception) versus returning a more descriptive value is a difference in calling convention, whereas the difference in pure code is greater since you need to be in IO to catch it. That's not a good reason either. My brainstorming cap goes on. I cannot come up with a good reason why openFile throws an exception when perfectly reasonable things happen, such as the file not existing. Maybe we need to step back a little and remember what exceptions were invented for. Exceptions are not necessary, explicit error handling works, and you'll find some C programmers with no problem with it. Exceptions are an abstraction over code that handles exceptional conditions. It designates a code path as special, the "normal" path, and assumes that in all other cases you want to abort the rest of the normal path up to some sequencing point. In my personal functional software design philosophy, I think that all functions (in a given interface, not talking about helpers) should be total. You shouldn't be able to pass a value to one of my functions and get an error unless you wrote the error message yourself (sorry, can't guard against fourierTransform (error "haha u were wrong")). Thus in my philosophy, head and tail are bad style, and I almost never use them. Exceptions are not the same as errors, exceptions are just a convenient notation for dealing with code paths that follow a common pattern. Errors just shouldn't happen, and I shouldn't use any non-total functions (unless I can prove that I'm using them correctly, which is quite a lot of work, it's easier just not to). MonadError is how we deal with that common pattern (it's a good way to do it), and there's Control.Exception.catch if we're dealing with code that is not so well-behaved. Unfortunately there is a lot of library code which is not so well behaved.
How precisely would you handle IndexError if it would be an exception and not just an error?
Well, to take a hypothetical example ... I have never looked into JPEG image decoding, but suppose that it's moderately complicated and further that it involves array indexing at some point, and I have written a web browser that needs to decode and display images. Maybe one in ten thousand JPEG images will be invalid for some reason, in a way that leads to an index beyond the bounds of its sequence.
Because we have the data at hand, we can certainly validate indices vs. bounds and avoid an index error. Then we need an Either or Maybe or some such thing to express that, and some way to propagate the issue up to the top of the image renderer. Whereupon it will refuse to render the image and will put its broken image icon in its place.
Or, I can just write the JPEG decoding algorithm, and catch index errors at that same place, and refuse to render the image etc.. To me, this doesn't pose any moral problem, and the code is bound to be clearer and more expressive of its core intention, than if it were burdened with layer upon layer of Rights and Justs to defend against a problem that has no real solution and may never even occur.
If the image I'm supposed to decode isn't actually there when I try to open the file, then I'll display the very same icon in its place. We want the same thing to happen, whatever the problem with the image: display the broken image icon, and go on to render the rest of the page. Now if we want to be philosophical about it, all of these problems don't have to be with the image - maybe it's a variant JPEG type that I didn't know about, or even just my coding error that happened to escape testing. The web browser should carry on regardless, however, so the implementation shouldn't be sensitive to these philosophical distinctions.
So here you don't trust your algorithm. And in that case Control.Exception.catch seems perfectly reasonable. If you don't like non-determinism, then you could put your computation in MonadError. But you'd have to wrap arrays to fail using, say, the monad pattern rather than erroring. That's kind of annoying, and sadly I think there's nothing to do but take it up with the library author. I'm just spouting nonsense... Luke

On Mon, 10 Mar 2008, Donn Cave wrote:
On Mar 9, 2008, at 1:07 PM, Henning Thielemann wrote:
How precisely would you handle IndexError if it would be an exception and not just an error?
Well, to take a hypothetical example ... I have never looked into JPEG image decoding, but suppose that it's moderately complicated and further that it involves array indexing at some point, and I have written a web browser that needs to decode and display images. Maybe one in ten thousand JPEG images will be invalid for some reason, in a way that leads to an index beyond the bounds of its sequence.
Ok let's examine this example. The decoding algorithm you describe is a non-IO function: JFIF.decode :: ByteString -> Image You expect that there are inputs that must be considered corrupt. You cannot return an image for corrupt input, thus the signature must be JFIF.decode :: ByteString -> Maybe Image Now there are two possibilities: Either your implementation works properly, then it should return Nothing for a corrupt JFIF input, or it works improperly and accesses non-existing array elements and fails with an error. If you encounter that, you must fix that error, it would not help to catch the error at run-time.
Or, I can just write the JPEG decoding algorithm, and catch index errors at that same place, and refuse to render the image etc.. To me, this doesn't pose any moral problem, and the code is bound to be clearer and more expressive of its core intention, than if it were burdened with layer upon layer of Rights and Justs to defend against a problem that has no real solution and may never even occur.
Internally you must check the array bounds, this might indeed mean to cope with Lefts and Rights. However you can process this with monad combinators and syntax and then looks like exception handling in IO code. If you want it to be handled the same way as 'file not found' you can lift the 'Left' to an IO exception. In my opinion, IO functions should also expose their exceptions by types, e.g. openFile :: FilePath -> IOMode -> IO (Either IOError Handle) openFile :: FilePath -> IOMode -> ErrorT IOError IO Handle
If the image I'm supposed to decode isn't actually there when I try to open the file, then I'll display the very same icon in its place. We want the same thing to happen, whatever the problem with the image: display the broken image icon, and go on to render the rest of the page. Now if we want to be philosophical about it, all of these problems don't have to be with the image - maybe it's a variant JPEG type that I didn't know about, or even just my coding error that happened to escape testing. The web browser should carry on regardless, however, so the implementation shouldn't be sensitive to these philosophical distinctions.
Indeed all the situations "file not found", "unsupported format version", "corrupt file content" are exceptions, you must handle them properly with Maybe, Either, ErrorT, or IO exceptions, but you should not invoke 'error'. 'error' is reserved for the case when you forget to check array bounds and thus a corrupt file would corrupt your program if the libraries default check wouldn't intervene. Actually, the array library calls 'error' for indexes out of bound, precisely because they violate the calling conditions for, say (!).

On 10 Mar 2008, at 12:37 AM, Donn Cave wrote:
On Mar 9, 2008, at 1:07 PM, Henning Thielemann wrote:
Errors are programming errors and must be fixed as Denis explained. Thus there is no need for a complex system of handling these situations at run-time. The program error might be unexpected but it isn't the fault of the user or of the context the program runs in but of the fault of the programmer. The program may report "bug detected, send an e-mail to the author" but eventually it should quit (at least the buggy thread) before worse things happen. This is possible in Haskell but should not be mixed up with handling of exceptions like "non-existing file".
I am not sure I see the difference in principle that you see.
An exception is, for me, any state that isn't properly accounted for in its immediate context. openFile could return 'Maybe Handle', but it doesn't, so the context demands a Handle or an exception.
In the context of this discussion, `Maybe Handle' /is/ an exception type, because Maybe is an exception monad. As is IO. This distinction is one between an unusual-but-anticipated code path, and a case the programmer simply didn't handle at all. The former is an exception; the latter is an error. jcc

On Mar 10, 2008, at 5:48 PM, Jonathan Cast wrote:
On 10 Mar 2008, at 12:37 AM, Donn Cave wrote:
...
An exception is, for me, any state that isn't properly accounted for in its immediate context. openFile could return 'Maybe Handle', but it doesn't, so the context demands a Handle or an exception.
In the context of this discussion, `Maybe Handle' /is/ an exception type, because Maybe is an exception monad. As is IO. This distinction is one between an unusual-but-anticipated code path, and a case the programmer simply didn't handle at all. The former is an exception; the latter is an error.
I am not sure I understand what you're saying. `Error' may be open to interpretation, but `exception' has a fairly unambiguous technical meaning in Haskell, a non-local flow of control with associated data type in Control.Exception. That flow of control involves `exception handlers', so named in the documentation, so if I install an exception handler in location that can intercept the exception, then you can't really say no one handled it, without some violence to the language. An exception, in the technical sense of the word, really can only be handled somewhere else, other than the exact location the exception was thrown, that being the nature and intent of non-local flow of control. What I'm trying to bring to this is that if we can let go of our fastidious notions about Maybe and Either and embrace what appears to be the reality of exceptions in Haskell that are more or less like other languages, then we'll be pleased at the improvement in the code. Here's a function I wrote a couple weeks ago while playing around with LDAP. I have read this ByteString from a socket, and verified that it's complete - it's all BER length encoded data, and the string is of the length specified in its length tag. Now I start breaking it down, according to my understanding of the Basic Encoding Rules and LDAP. The `berList' function handles the BER part, and this is the first LDAP step: readLDAPMessage s = let [(_, msgID), (tag, body)] = berList s in LDAPMessage (berInt msgID) (readResponse tag body) I go on to account for all the LDAP stuff I need in about 60 lines of that kind of thing, 1/3 of it devoted to declarations of the data types, and it isn't dense code, it's ... essentially declarative, in a simple, straightforward way, almost as if I copied it directly from the RFC. Is it `total'? No way! To get there, it seems to me I'd have to double the code, and significantly distract from its real sense. If there were anything to be gained, sure it's worth the price, but there isn't! Recall that this data comes from a socket. On the other end, anyone who cares about survival has an exception handler. I could gather up layers of Lefts and Rights and return a Left blah, and it really only adds another failure mode for the caller. I'm not saying Either is bad - I do use it like this, when it seems to be called for, but I'm very glad that it isn't the only way to deal with errors originating in functional code. I had the contrary impression from a discussion some years back, where I see now in review I may have conflated what you can't do in Haskell 98, with what you can't do in pure code for theoretical reasons. (Though I'm not saying that the argument for the latter was persuasive. I think handing exceptions in pure code is just the next step.) Donn Cave, donn@avvanta.com

On Tue, 11 Mar 2008, Donn Cave wrote:
On Mar 10, 2008, at 5:48 PM, Jonathan Cast wrote:
On 10 Mar 2008, at 12:37 AM, Donn Cave wrote: ...
An exception is, for me, any state that isn't properly accounted for in its immediate context. openFile could return 'Maybe Handle', but it doesn't, so the context demands a Handle or an exception.
In the context of this discussion, `Maybe Handle' /is/ an exception type, because Maybe is an exception monad. As is IO. This distinction is one between an unusual-but-anticipated code path, and a case the programmer simply didn't handle at all. The former is an exception; the latter is an error.
Thanks for that summary!
I am not sure I understand what you're saying. `Error' may be open to interpretation, but `exception' has a fairly unambiguous technical meaning in Haskell, a non-local flow of control with associated data type in Control.Exception.
The TRY construct for Maybe is called 'maybe', the exception handler is the first argument to 'maybe'. The absence of 'Maybe' after calling 'maybe' shows that the exception was handled.

On Mar 11, 2008, at 14:27 , Donn Cave wrote:
readLDAPMessage s = let [(_, msgID), (tag, body)] = berList s in LDAPMessage (berInt msgID) (readResponse tag body)
I go on to account for all the LDAP stuff I need in about 60 lines of that kind of thing, 1/3 of it devoted to declarations of the data types, and it isn't dense code, it's ... essentially declarative, in a simple, straightforward way, almost as if I copied it directly from the RFC.
Is it `total'? No way! To get there, it seems to me I'd have to double the code, and significantly distract from its real sense.
You might want to think about the monadic use of Maybe/Either (or more generally MonadError), which abstracts away the checking and tracking into (>>=). The error handler is then at the point where values are injected into / retrieved from the monadic exception, similar to catch (...). -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

On Mar 12, 2008, at 6:34 AM, Brandon S. Allbery KF8NH wrote:
On Mar 11, 2008, at 14:27 , Donn Cave wrote:
readLDAPMessage s = let [(_, msgID), (tag, body)] = berList s in LDAPMessage (berInt msgID) (readResponse tag body)
I go on to account for all the LDAP stuff I need in about 60 lines of that kind of thing, 1/3 of it devoted to declarations of the data types, and it isn't dense code, it's ... essentially declarative, in a simple, straightforward way, almost as if I copied it directly from the RFC.
Is it `total'? No way! To get there, it seems to me I'd have to double the code, and significantly distract from its real sense.
You might want to think about the monadic use of Maybe/Either (or more generally MonadError), which abstracts away the checking and tracking into (>>=). The error handler is then at the point where values are injected into / retrieved from the monadic exception, similar to catch (...).
Sure. It isn't a lot of code, so I subjected it to Either-ization as an experiment, and I did indeed take the monad procedural route. The example function above became readLDAPMessage s = do [(_, msgID), (tag, body)] <- berList s >>= exactLen 2 i <- berInt msgID r <- readResponse tag body return (LDAPMessage i r) ... and the end result, applying this style across a number of related functions, was no more than half again as much code, and I guess not severely unreadable. Maybe it depends on whether a procedural style suits you. There may be clever ways to torture this logic into an even smaller format, but since the original is the clearest expression of the protocol and its caller is almost guaranteed to have an exception handler anyway -- in my opinion, it was a silly exercise, I'll throw the code away. The Either version forces strict evaluation, true? Let's say for some reason the caller actually uses only the first part, the LDAP message ID, then we don't really need to validate and decode the whole message, but if I were to Either-ize it, then it has to go the whole distance before we know it's Right and not Left? And likewise for every value of every attribute in the message. What I naively picture as the next step, where pure code can handle exceptions, is an _implicit_ evaluation monad like Either. Pattern matches then throw exceptions in the sense described in Control.Monad.Error, which may be caught in pure code by optionally making the monadic type explicit, or otherwise are converted to exceptions in the sense of Control.Exception and caught in the IO monad. I suppose this would not force evaluation, because it's fine grained - I don't fold (Right i) and (Right r) into (Right (LDAPMessage i r)), etc., but rather you may encounter a (Left _) anywhere in there. At least this would let us rationalize the use of 'exception' with two radically different meanings between Control.Monad.Error and Control.Exception. Donn Cave, donn@avvanta.com

On Mar 12, 2008, at 14:17 , Donn Cave wrote:
Sure. It isn't a lot of code, so I subjected it to Either-ization as an experiment, and I did indeed take the monad procedural route.
Monad != procedural, unless you insist on do notation. Think of it as composition (it may be easier to use (=<<) which "points the same direction" as (.)). -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

On Mar 12, 2008, at 12:32 PM, Brandon S. Allbery KF8NH wrote:
On Mar 12, 2008, at 14:17 , Donn Cave wrote:
Sure. It isn't a lot of code, so I subjected it to Either-ization as an experiment, and I did indeed take the monad procedural route.
Monad != procedural, unless you insist on do notation. Think of it as composition (it may be easier to use (=<<) which "points the same direction" as (.)).
Yes, I insist on do notation, because it provides a convenient binding form that works with what I'm doing - the original functional variation wasn't so suited to composition either, and used `let'. But I see that as only syntactic - equally procedural, either way. Expressions are evaluated in a fixed order, so seems inherently procedural to me and `do' is only a notational convenience. Donn Cave, donn@avvanta.com

On Wed, 12 Mar 2008, Donn Cave wrote:
On Mar 12, 2008, at 12:32 PM, Brandon S. Allbery KF8NH wrote:
On Mar 12, 2008, at 14:17 , Donn Cave wrote:
Sure. It isn't a lot of code, so I subjected it to Either-ization as an experiment, and I did indeed take the monad procedural route.
Monad != procedural, unless you insist on do notation. Think of it as composition (it may be easier to use (=<<) which "points the same direction" as (.)).
Yes, I insist on do notation, because it provides a convenient binding form that works with what I'm doing - the original functional variation wasn't so suited to composition either, and used `let'.
But I see that as only syntactic - equally procedural, either way. Expressions are evaluated in a fixed order,
Do notation only looks like there are statements that are processed from the beginning to the end. But that's not true, it's still purely lazy and expressions are evaluated in the order that is forced by data dependencies. I have added this issue to http://haskell.org/haskellwiki/Do_notation_considered_harmful

On Mar 12, 2008, at 2:10 PM, Henning Thielemann wrote:
On Wed, 12 Mar 2008, Donn Cave wrote:
On Mar 12, 2008, at 12:32 PM, Brandon S. Allbery KF8NH wrote:
Sure. It isn't a lot of code, so I subjected it to Either-ization as an experiment, and I did indeed take the monad procedural route. Monad != procedural, unless you insist on do notation. Think of it as composition (it may be easier to use (=<<) which "points
On Mar 12, 2008, at 14:17 , Donn Cave wrote: the same direction" as (.)).
Yes, I insist on do notation, because it provides a convenient binding form that works with what I'm doing - the original functional variation wasn't so suited to composition either, and used `let'.
But I see that as only syntactic - equally procedural, either way. Expressions are evaluated in a fixed order,
Do notation only looks like there are statements that are processed from the beginning to the end. But that's not true, it's still purely lazy and expressions are evaluated in the order that is forced by data dependencies.
Let me put it this way: if I write do (i, s') <- decodeInt s (v, _) <- decodeInt s' return (i, v) ... instead of, to just avoid the monad stuff case (decodeInt s) of Left e -> Left e Right (i, s') -> case (decodeInt s') of Left e -> Left e Right (v, _) -> Right (i, v) ... the `do' notation just emphasizes the procedural-ness of this computation. I can't arrive at the end, without completing these steps, whatever notation I choose. `do' just happens to be considerably more convenient. Of course data dependencies force the order of evaluation, but some of those dependencies are inevitably built into the expression: I can't evaluate the result (Right (i, _)) without evaluating the second decodeInt far enough to know that it isn't (Left _). The type that we'd need for that would be something like Either String (Int, (Either String Int)) (etc. ad nauseum as the return value gets more complex.) Well, the problem inherently requires a certain order of evaluation. But if you will just handle pattern match failure in the IO monad, then you can write a simple functional expression of the problem instead, let (i, s') = decodeInt s in let (v, _) = decodeInt s' in (i, v) ... where I think `i' can be evaluated without forcing unnecessary evaluation of v. It's clearer, and avoids unnecessary strictness! Donn Cave, donn@avvanta.com

On Wed, Mar 12, 2008 at 4:45 PM, Donn Cave
Well, the problem inherently requires a certain order of evaluation. But if you will just handle pattern match failure in the IO monad, then you can write a simple functional expression of the problem instead,
let (i, s') = decodeInt s in let (v, _) = decodeInt s' in (i, v)
... where I think `i' can be evaluated without forcing unnecessary evaluation of v. It's clearer, and avoids unnecessary strictness!
Unless of course you don't have an IO monad handy. The issue is that exception handling semantics do induce an order of evaluation for determinacy: if both functions in a composition throw an exception at some point (say in the 3rd element of a list they're generating), you need to decide which exception ends up being thrown, and to do that based on the lazy evaluation order would break referential transparency. It bugs me a little how good Haskell is with laziness, but how bad it gets when you need laziness with a slightly different structure. I don't see any way it could reasonably be "fixed" if I were designing my own language, it's just unsettling. Luke

On Mar 12, 2008, at 11:23 PM, Luke Palmer wrote:
The issue is that exception handling semantics do induce an order of evaluation for determinacy: if both functions in a composition throw an exception at some point (say in the 3rd element of a list they're generating), you need to decide which exception ends up being thrown, and to do that based on the lazy evaluation order would break referential transparency.
OK, that is what I was getting several years ago when I brought this up, but ... check my reasoning on this. Maybe a contrived example of what you're talking about, if I write f _ [] = [] f n (a:x) = (div 3 n, j a):(f (n - 1) x) where j (Just v) = v main = print $ f 2 [Just 1, Just 2, Nothing] Running it, I'll hear about a divide by zero on the 3rd element, and not hear about the Just match failure on the 3rd element. Suppose you model exceptions with an data type X that implicitly describes all expressions in Haskell: data X a = Good a | Bad String such that a partial explicit expansion of the above is f _ [] = [] f n (a:x) = (div 3 n, j a):(f (n - 1) x) where j (Just v) = Good v j Nothing = Bad "X.hs:15:0-27: Non-exhaustive patterns in function j" div a 0 = Bad "divide by zero" div a b = Good (div a b) The expanded result of (f 2 [Just 1, Just 2, Nothing]) will be [(Good 1, Good 1), (Good 3, Good 2), (Bad "divide by zero", Bad "X.hs:15:0-27: Non-exhaustive patterns in function j")] My IO action (print) had to decide to throw "divide by zero", but arbitrary decisions like that are common and don't make the underlying principle unsound. Am I missing something, or is that a suitable model for pure, non-strict exceptions that could in principle be caught outside IO? (Of course, I do _not_ propose to write code per the expanded example above - that's only a partial expansion, and already too cumbersome to be useful. It's only a conceptual model.) Donn Cave, donn@avvanta.com

Luke Palmer wrote:
On Wed, Mar 12, 2008 at 4:45 PM, Donn Cave
wrote: Well, the problem inherently requires a certain order of evaluation. But if you will just handle pattern match failure in the IO monad, then you can write a simple functional expression of the problem instead,
let (i, s') = decodeInt s in let (v, _) = decodeInt s' in (i, v)
... where I think `i' can be evaluated without forcing unnecessary evaluation of v. It's clearer, and avoids unnecessary strictness!
Unless of course you don't have an IO monad handy.
The issue is that exception handling semantics do induce an order of evaluation for determinacy: if both functions in a composition throw an exception at some point (say in the 3rd element of a list they're generating), you need to decide which exception ends up being thrown, and to do that based on the lazy evaluation order would break referential transparency.
It bugs me a little how good Haskell is with laziness, but how bad it gets when you need laziness with a slightly different structure. I don't see any way it could reasonably be "fixed" if I were designing my own language, it's just unsettling.
Isn't this what "imprecise exeptions" were invented (and implemented in GHC) for? See http://research.microsoft.com/Users/simonpj/Papers/imprecise-exn.htm Cheers Ben

On Wed, 12 Mar 2008, Donn Cave wrote:
On Mar 12, 2008, at 2:10 PM, Henning Thielemann wrote:
On Wed, 12 Mar 2008, Donn Cave wrote:
On Mar 12, 2008, at 12:32 PM, Brandon S. Allbery KF8NH wrote:
Sure. It isn't a lot of code, so I subjected it to Either-ization as an experiment, and I did indeed take the monad procedural route. Monad != procedural, unless you insist on do notation. Think of it as composition (it may be easier to use (=<<) which "points the same
On Mar 12, 2008, at 14:17 , Donn Cave wrote: direction" as (.)).
Yes, I insist on do notation, because it provides a convenient binding form that works with what I'm doing - the original functional variation wasn't so suited to composition either, and used `let'.
But I see that as only syntactic - equally procedural, either way. Expressions are evaluated in a fixed order,
Do notation only looks like there are statements that are processed from the beginning to the end. But that's not true, it's still purely lazy and expressions are evaluated in the order that is forced by data dependencies.
Let me put it this way: if I write
do (i, s') <- decodeInt s (v, _) <- decodeInt s' return (i, v)
... instead of, to just avoid the monad stuff
case (decodeInt s) of Left e -> Left e Right (i, s') -> case (decodeInt s') of Left e -> Left e Right (v, _) -> Right (i, v)
Since the decision between Left and Right requires all parts to be evaluated, it's Either that might too strict for your application. What about a writer monad, where exceptions, or better say warnings, are written to the writer stream?
participants (10)
-
Ben Franksen
-
Brandon S. Allbery KF8NH
-
Denis Bueno
-
Don Stewart
-
Donn Cave
-
Henning Thielemann
-
Jonathan Cast
-
Luke Palmer
-
Sterling Clover
-
Xiao-Yong Jin