Version française
Home     About     Download     Resources     Contact us    

This site is updated infrequently. For up-to-date information, please visit the new OCaml website at

Browse thread
[Caml-list] Exceptions considered harmful
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: 2004-06-29 (01:02)
From: skaller <skaller@u...>
Subject: [Caml-list] Exceptions considered harmful
On Tue, 2004-06-29 at 03:34, Markus Mottl wrote:
> On Tue, 29 Jun 2004, Martin Jambon wrote:
> > This makes me feel like we will not know if the "finally block" has been
> > completed or not. Why not totally prohibiting any exception raised from
> > the "finally block"?
> > 
> > let protect f x finally =
> >   let res =
> >     try f x
> >     with exc ->
> >       (try finally x with _ -> invalid_arg "protect")
> >       raise exc in
> >   (try finally x with _ -> invalid_arg "protect");
> >   res
> But how do you know then that the exception came from "finally" and not
> from "f"?  You could even lose the information, whether "f" was executed
> successfully at all!

In general, I think exceptions are a bad idea.
I spend a lot of time removing them from my code :(

In a typical case it goes like this:

  let f = open_in fname in
  do_something f;
  close_in f
with _ -> ()

Only this is very BAD code because it fails to isolate
two completely distinct possibilities for an error:
in opening the file, or in processing it: in the latter
case the file isn't closed: we didn't actually
expected a processing error here, and also fail to
correctly re-raise the exception.

This lack of localisation is quite hard to fix:

  let f = try open_in fname with _ -> () in
  do_something f;
  close_in f

WOOPS! that's a type error, which is good, because
we can't 'do_something' if the file wasn't opened.
What else can we try?

    let f = open_in fname in
      try do_something f
      with _ -> close_in f 
    close_in f;
  with _ -> failwith "Do something failed"

WOOPS, we tried to close f twice. We can only do this:

  exception Some_error
    let f = open_in fname in
      try do_something f
      with _ -> close_in f; raise Some_error
    close_in f;
   with Some_error -> raise Some_error
   | _ -> ()

but now we have lost the location and kind of the
error in something .. and we had to introduce a new
global exception as well. 

The obvious way to fix this mess is to eliminate the 
undesirable exceptions early:

  let result = 
    try Some (open_in fname) with _ -> None 
  in match result with
  | None -> ()
  | Some f -> 
    begin try do_something with x -> close_in f; raise x end
    close_in f

Of course, the result of the calculation here is unit,
more generally we'd be saying 

let result = do_something in close_in f; result

and obviously we'd be wrapping that as well.

Generally, the exceptions discard localisation
and destroy any kind of static assurance errors are handled.

To me, the Haskell monadic approach seems a sounder.

The problem with exceptions is that they're basically
without solid theoretical principles. Throwing out 
context is one thing .. recovering at an unknown
point, trapping unknown exceptions without any
aid from the type system simply defeats the purpose
of having a type system.

As far as I can see, just about the only way to
use exceptions properly is to eliminate them
at the earliest possible point .. which suggests
libraries simply shouldn't throw them.

It seems difficult to escape from deep recursions
without exceptions. They can be convenient.
They're useful for rapid prototyping where you
need a demo that works on some cases *fast*.

And they seem convenient when you 'know' there
can't be an error. The price is high. In avoiding
statically handling false negatives (errors that
can't occur) your code has become fragile: any change
which may in fact lead to raising an error will
not be discovered without testing.. and then it
will be very hard to find.

Would static exceptions be better? (Allow exceptions
but require that they're caught in an ancestor of the
scope they're raised in, and at least in a child of
the scope the exception is declared in)

Any comments on any of this appreciated.

John Skaller,
voice: 061-2-9660-0850, 
snail: PO BOX 401 Glebe NSW 2037 Australia
Checkout the Felix programming language

To unsubscribe, mail Archives:
Bug reports: FAQ:
Beginner's list: