On Mon, Mar 10, 2008 at 7:37 AM, Donn Cave <donn at avvanta.com> 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. '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