Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Re: [Caml-list] [OSR] Exceptionless error management

11 views
Skip to first unread message

Till Varoquaux

unread,
Jan 31, 2008, 4:58:03 AM1/31/08
to Bünzli Daniel, caml-list List
My cocan account is not created yet soo I will answer here:
_ In the thread you are pointing to Xavier did not say using
exceptions was a mistake, he said the Failure exception (such as
int_of_string) was legacy code and not great.
_ Allowing Invalid_argument seems a little to hardcore. When doing
system programming for instance there is an incredible range of
exceptional events that can happen, you are definitly not going to
handle all of them (or are you?) so you will need to let them trickle
up. This will eventually require using something akin to an error
monad and some awfull mangling with polymorpic variants.
_Exceptions are terse. Suppose Map.find returned an option type (and
this seems a sensible choice). If you were in a place where not being
in the map really was an exceptional event you'd need to unbox and do
some error handling for every lookup (and since you do not want to
raise an exception I must wonder what you would do). Not only is this
cumbersome, but it also is bound to be slow and lead to some loss of
information (no more printexc, backtrace...) unless you jump through a
lot of hoops.

Here is my take:
_You should try to keep exceptions for exceptional cases. If you think
you might need to catch them do not hesitate to create your own
exception.
_For functions like find... declare two functions: one that boxes it's
return value (i.e val input_line : in_channel -> string option) and
another that raises an exception (i.e val input_line_exn : in_channel
-> string).

Till
On Jan 31, 2008 8:55 AM, Bünzli Daniel <daniel....@erratique.ch> wrote:
> Hello,
>
> Based on discussions and the resulting consensus that occured more
> than once on this list. I wrote a recommendation on how modules should
> perform error management. It is available here :
>
> http://cocan.org/osr/exceptionless_error_management
>
> Best,
>
> Daniel
>
> _______________________________________________
> Caml-list mailing list. Subscription management:
> http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list
> Archives: http://caml.inria.fr
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs
>

--
http://till-varoquaux.blogspot.com/

Bünzli Daniel

unread,
Jan 31, 2008, 6:02:02 AM1/31/08
to Till Varoquaux, caml-list List
Le 31 janv. 08 à 10:57, Till Varoquaux a écrit :

> My cocan account is not created yet soo I will answer here:

I think it is better to discuss on the list.

> _ In the thread you are pointing to Xavier did not say using
> exceptions was a mistake, he said the Failure exception (such as
> int_of_string) was legacy code and not great.

Right, corrected.

> _ Allowing Invalid_argument seems a little to hardcore. When doing
> system programming for instance there is an incredible range of
> exceptional events that can happen, you are definitly not going to
> handle all of them (or are you?) so you will need to let them trickle
> up. This will eventually require using something akin to an error
> monad and some awfull mangling with polymorpic variants.

This OSR is mainly targeted at reusable modules and it says nothing
about clients of the module. Clients are allowed to define their own
exceptions to propagate the error up. The contract of a conforming
module is that it won't interfere with the client's control flow as
long as it behaves correctly.

> _Exceptions are terse. Suppose Map.find returned an option type (and
> this seems a sensible choice). If you were in a place where not being
> in the map really was an exceptional event you'd need to unbox and do
> some error handling for every lookup (and since you do not want to
> raise an exception I must wonder what you would do).

They are terse because they are implicit and I don't like this. For me
either not being in the map can happen and then you have to deal with
it at each call because of program correctness either you know, e.g.
by construction, that not being in the map cannot happen. In the
latter case I would define the following local function :

let find map k = match Map.find map k with Some v -> v | None ->
assert false

> _For functions like find... declare two functions: one that boxes it's
> return value (i.e val input_line : in_channel -> string option) and
> another that raises an exception (i.e val input_line_exn : in_channel
> -> string).

Requiring this clutters apis and is more work for the module
developer. If you want a function that raises you can define your own
local function as suggested above.

Andrej Bauer

unread,
Jan 31, 2008, 9:10:07 AM1/31/08
to Bünzli Daniel, caml-list List
Bünzli Daniel wrote:
> let find map k = match Map.find map k with Some v -> v | None -> assert
> false

I have become to prefer option types as return values (as opposed to
exceptions), but I admit it can be annoying to always consider both
possibilities, especially if you know that "None" won't happen. If the
library only provides "find" that returns an option type, the above
solution gets around constant checking. But how much runtime overhead
does it cause? Has anyone measured that?

What is wrong with having two functions? With a bit of imagination, we
can give them reasonable names, e.g., get and search. Everybody can
guess which one returns the option type and which one throws an
exception, right?

Throwing out functionality on the grounds that it "clutters APIs"
doesn't sound like a good idea to me. Throwing out bells and whistles is
another matter.

Andrej

Michael Ekstrand

unread,
Jan 31, 2008, 9:16:43 AM1/31/08
to caml...@inria.fr
Andrej Bauer <Andrej...@fmf.uni-lj.si> writes:
> Bünzli Daniel wrote:
>> let find map k = match Map.find map k with Some v -> v | None ->
>> assert false
>
> I have become to prefer option types as return values (as opposed to
> exceptions), but I admit it can be annoying to always consider both
> possibilities, especially if you know that "None" won't happen. If the
> library only provides "find" that returns an option type, the above
> solution gets around constant checking. But how much runtime overhead
> does it cause? Has anyone measured that?
>
> What is wrong with having two functions? With a bit of imagination, we
> can give them reasonable names, e.g., get and search. Everybody can
> guess which one returns the option type and which one throws an
> exception, right?

While `get' and `search' are probably OK (I presume that `get' raises
an exception, and `search' returns an option type), we must be
careful. If multiple functions is the standard, I would prefer that a
naming/variant system be recommended (such as get and get_exn). If we
come up with clever names for our various functions, I fear the Java
Queue API syndrome (where you can get a value with any of 4 different
functions, and add with another 4, that mostly differ in how they
handle error and thread blocking conditions and the names have little
correlation to the differences).

- Michael

--
mouse, n: A device for pointing at the xterm in which you want to type.
Confused by the strange files? I cryptographically sign my messages.
For more information see <http://www.elehack.net/resources/gpg>.

David Teller

unread,
Jan 31, 2008, 2:28:31 PM1/31/08
to Michael Ekstrand, caml...@inria.fr
Differentiating functions sounds like a good idea.

We may either:
* add a suffix after function names whenever they may raise exceptions
(suffix "_exn" sounds good to me)
* add a suffix after function names whenever they are variants that
don't raise exceptions (say, suffix "_maybe")
* write two different (sub)modules with the same set of functions, one
module being named Exception, the other one being named Option.

What else ? We could presumably functorize modules upon the definition
of a error-delivery mechanism, but that might be overkill.

Say
module type ErrorMechanism = sig
type 't; (**The type of a failure.*)
type 'a can_fail;
(**The type of a value that can either succeed
and produce a result of type 'a or fail and
produce an error of type t. *)
value fail : t -> 'a can_fail ;
(**Fail. Depending on the mechanism, this may
either return some variant on option or
throw an exception.*)

value succeed : 'a -> 'a can_fail ;
(**Succeed.*)
end;;

We could also introduce an exception monad which would be an
intermediate choice between using exceptions and using option types and
such. I have a just written a prototype for this, I'll try and release
it soon.

Cheers,
David

On Thu, 2008-01-31 at 08:16 -0600, Michael Ekstrand wrote:
> While `get' and `search' are probably OK (I presume that `get' raises
> an exception, and `search' returns an option type), we must be
> careful. If multiple functions is the standard, I would prefer that a
> naming/variant system be recommended (such as get and get_exn). If we
> come up with clever names for our various functions, I fear the Java
> Queue API syndrome (where you can get a value with any of 4 different
> functions, and add with another 4, that mostly differ in how they
> handle error and thread blocking conditions and the names have little
> correlation to the differences).
>
> - Michael

--
David Teller
Security of Distributed Systems
http://www.univ-orleans.fr/lifo/Members/David.Teller
Angry researcher: French Universities need reforms, but the LRU act
brings liquidations.

Michael Ekstrand

unread,
Jan 31, 2008, 3:00:17 PM1/31/08
to David Teller, caml...@inria.fr
David Teller <David....@univ-orleans.fr> writes:
> What else ? We could presumably functorize modules upon the definition
> of a error-delivery mechanism, but that might be overkill.
>
> Say
> module type ErrorMechanism = sig
> type 't; (**The type of a failure.*)
> type 'a can_fail;
> (**The type of a value that can either succeed
> and produce a result of type 'a or fail and
> produce an error of type t. *)
> value fail : t -> 'a can_fail ;
> (**Fail. Depending on the mechanism, this may
> either return some variant on option or
> throw an exception.*)
>
> value succeed : 'a -> 'a can_fail ;
> (**Succeed.*)
> end;;

This could have some interesting ramifications, such as aiding in the
construction of a condition/restart mechanism.

However, standard instantiations of these functorized modules should
probably be provided to make the common case (using the module with
one or the other standard behavior) easy.

- Michael

--
mouse, n: A device for pointing at the xterm in which you want to type.
Confused by the strange files? I cryptographically sign my messages.
For more information see <http://www.elehack.net/resources/gpg>.

_______________________________________________

Bünzli Daniel

unread,
Jan 31, 2008, 3:04:39 PM1/31/08
to Andrej...@andrej.com, caml-list List

Le 31 janv. 08 à 15:09, Andrej Bauer a écrit :

> I have become to prefer option types as return values (as opposed to
> exceptions), but I admit it can be annoying to always consider both
> possibilities, especially if you know that "None" won't happen.

Maybe it is a point of view, you are fed up of considering both
possibilites while I'm personally fed up of writing

match try Some (f x) with e -> None with
| None -> ..
| Some v -> ...

Because I need tail recursive functions.

> If the library only provides "find" that returns an option type, the
> above solution gets around constant checking. But how much runtime
> overhead does it cause? Has anyone measured that?

No idea. Unscientific benchmark at the end of the email.

> What is wrong with having two functions?

You have to choose. You don't have exceptionless error mangement
anymore which is the point of the proposal. As the module developer I
don't want to write two functions to do the same thing albeit in a
slight different way one of which strikes me as the being the wrong way.

Best,

Daniel

osx 10.5.1 1ghz G4 512 ram

> cat in.ml
let () =
let f = ref [] in
try while (true) do f := (input_line stdin) :: !f done
with End_of_file -> ignore (!f)

> cat inb.ml
let () =
let rec next acc =
match try Some (input_line stdin) with End_of_file -> None with
| None -> acc
| Some l -> next (l :: acc)
in
ignore (next [])

> ocamlbuild in.native inb.native
Finished, 8 targets (0 cached) in 00:00:01.

> time jot 1000000 | ./in.native

real 0m4.814s
user 0m4.371s
sys 0m0.218s

> time jot 1000000 | ./inb.native

real 0m4.790s
user 0m4.355s
sys 0m0.217s

blue storm

unread,
Jan 31, 2008, 3:06:41 PM1/31/08
to David Teller, caml...@inria.fr
My two cents :

- It's easy to derive variant-defined functions from the
exception-using ones, with virtually no performance cost. Going the
other way is not as practical, and i assume it has a higher runtime
overhead
- We will need to document our functions in any case : the exception
name generally doesn't provide an accurate enough description of the
causes of the error (be it an exception, a polymorphic variant, ...)
- The added safety is good, but not (in my eyes) essential for every
use; we could use tools, eg. OcamlExc (
http://caml.inria.fr/pub/old_caml_site/ocamlexc/ocamlexc.htm ) to
check the safety as well
- In most situation, exceptions allow for a more natural and more
concise code than options/variants/monads, precisely because they can
break flow of control (eg. in a "for" loop), and because our libs are
not quite adapted to that kind of programming now (where is mapM ?)

I would favor the conservative solution : still provide the exception
handling by default, and allowing an additional option/variants
version.

David Teller

unread,
Jan 31, 2008, 3:26:19 PM1/31/08
to Bünzli Daniel, caml-list List, Andrej...@andrej.com

On Thu, 2008-01-31 at 21:03 +0100, Bünzli Daniel wrote:
> Le 31 janv. 08 à 15:09, Andrej Bauer a écrit :
>
> > I have become to prefer option types as return values (as opposed to
> > exceptions), but I admit it can be annoying to always consider both
> > possibilities, especially if you know that "None" won't happen.
>
> Maybe it is a point of view, you are fed up of considering both
> possibilites while I'm personally fed up of writing
>
> match try Some (f x) with e -> None with
> | None -> ..
> | Some v -> ...
>
> Because I need tail recursive functions.

What about a generic

type result 'r 'e =
| Success of 'r (**The operation was a success*)
| Failure of 'e (**The operation was a failure*)

let purify e =
try
Success ( Lazy.force e )
with
exc -> Failure exc

?

With one such mechanism we can nearly automatically transform
exception-returning expressions into exceptionless management. We could
complete this with a trivial amount of Camlp4 code to avoid the call to
"lazy / Lazy.force".

What do you think about this solution ?

Cheers,
David

--
David Teller
Security of Distributed Systems
http://www.univ-orleans.fr/lifo/Members/David.Teller
Angry researcher: French Universities need reforms, but the LRU act
brings liquidations.

_______________________________________________

David Teller

unread,
Jan 31, 2008, 3:41:01 PM1/31/08
to Bünzli Daniel, caml-list List, Andrej...@andrej.com
On Thu, 2008-01-31 at 21:25 +0100, David Teller wrote:
> What about a generic
>
> type result 'r 'e =
> | Success of 'r (**The operation was a success*)
> | Failure of 'e (**The operation was a failure*)
>
> let purify e =
> try
> Success ( Lazy.force e )
> with
> exc -> Failure exc
>
> ?

Oh, yeah, "Failure" was perhaps the wrong name to choose, as that's
already the name of an exception. So please replace occurrences of
"Failure" with "Failed" in the previous message.

And to answer a possibly incoming comment about 'e, yeah, it isn't
needed for exceptions, although I suspect it can come in handy for
operations that are exceptionless in the first place.

Bünzli Daniel

unread,
Jan 31, 2008, 4:16:34 PM1/31/08
to caml-list List

Le 31 janv. 08 à 21:25, David Teller a écrit :

> What do you think about this solution ?

I have no love for camlp4 and I don't like too much infrastructure. As
I already said I'm not really interested in having both.The
recommendation as given has the advantage that it doesn't introduce
any new type in the standard library or require external support. It
is just a design guideline. Actually maybe this should have been
called an ODG (ocaml design guideline) since as other have pointed out
on the wiki there are extreme cases (like the Unix module) where you
don't want exceptionless error management and I agree. Still, I think
this recommendation is widely applicable and should be used for most
(new) modules and I wanted to be able to put a name on it. However as
always use your judgement too see if it really fits.

Best,

Daniel

David Teller

unread,
Jan 31, 2008, 4:31:22 PM1/31/08
to Bünzli Daniel, caml-list List
On Thu, 2008-01-31 at 22:16 +0100, Bünzli Daniel wrote:
> Le 31 janv. 08 à 21:25, David Teller a écrit :
>
> > What do you think about this solution ?
>
> I have no love for camlp4 and I don't like too much infrastructure.

Well, it's 5 lines of infrastructure, written once and for all, and
which does solve your source of irritation with try. So I can only
assume that it doesn't qualify as "too much infrastructure" :)

> The
> recommendation as given has the advantage that it doesn't introduce
> any new type in the standard library or require external support.

Which recommendation is that, for the moment ?

Cheers,
David

--
David Teller
Security of Distributed Systems
http://www.univ-orleans.fr/lifo/Members/David.Teller
Angry researcher: French Universities need reforms, but the LRU act brings liquidations.

_______________________________________________

Christophe Raffalli

unread,
Jan 31, 2008, 5:01:36 PM1/31/08
to David Teller, Bünzli Daniel, caml-list List, Andrej...@andrej.com

>> match try Some (f x) with e -> None with
>> | None -> ..
>> | Some v -> ...
>>
>> Because I need tail recursive functions.
>>
>
>
For tail call and exception, the best is to keep exception and use "let
try" contruct
(a camlp4 extension is available, but the coding is quite simple):

let try x = expr in
normal_case
with
e -> exc_case

is translated to

(try
let x = expr in
fun () -> normal_case
with
e ->
fun () -> exc_case) ()

This way you get tail call in norma_case and exc_case.

Hope this helps,
Christophe

--
Christophe Raffalli
Universite de Savoie
Batiment Le Chablais, bureau 21
73376 Le Bourget-du-Lac Cedex

tel: (33) 4 79 75 81 03
fax: (33) 4 79 75 87 42
mail: Christoph...@univ-savoie.fr
www: http://www.lama.univ-savoie.fr/~RAFFALLI
---------------------------------------------
IMPORTANT: this mail is signed using PGP/MIME
At least Enigmail/Mozilla, mutt or evolution
can check this signature. The public key is
stored on www.keyserver.net
---------------------------------------------

Christophe_Raffalli.vcf
signature.asc

Jon Harrop

unread,
Jan 31, 2008, 6:50:20 PM1/31/08
to caml...@yquem.inria.fr
On Thursday 31 January 2008 20:25:41 David Teller wrote:
> What about a generic
>
> type result 'r 'e =
>
> | Success of 'r (**The operation was a success*)
> | Failure of 'e (**The operation was a failure*)
>
> let purify e =
> try
> Success ( Lazy.force e )
> with
> exc -> Failure exc
>
> ?
>
> With one such mechanism we can nearly automatically transform
> exception-returning expressions into exceptionless management. We could
> complete this with a trivial amount of Camlp4 code to avoid the call to
> "lazy / Lazy.force".
>
> What do you think about this solution ?

Scrap the explicitly-declared sum type for a polymorphic variant, lazy value
for a function and catch only a specific exception and you've got the
approach that I already use.

You can factor this out but this is so rarely used that it isn't worth it. If
the stdlib had common IO functions I wouldn't use that idiom at all.

--
Dr Jon D Harrop, Flying Frog Consultancy Ltd.
http://www.ffconsultancy.com/products/?e

Michaël Grünewald

unread,
Feb 1, 2008, 2:28:23 AM2/1/08
to Bünzli Daniel, caml-list List, Andrej...@andrej.com
Bünzli Daniel <daniel....@erratique.ch> writes:

>> time jot 1000000 | ./in.native
>
> real 0m4.814s
> user 0m4.371s
> sys 0m0.218s

You are timing `jot' aren't you?

>> time jot 1000000 | ./inb.native
>
> real 0m4.790s
> user 0m4.355s
> sys 0m0.217s

The recommendation you proposed would be even more useful if it
emphasizes when ``Exceptionless error management'' is adequate. You
can rewrite last sentence of first paragraph

``The following points suggest that raising exceptions to signal
unexceptional error cases is not a good idea.''

like this:

``Let's enumerate situations where raising exceptions to signal
unexceptional error cases is not a good idea.''

Maybe would you also suppress the `bad' in the first sentence, since
it is `bad' with regard to the situations you are enumerating.
--
Michaël

Bünzli Daniel

unread,
Feb 1, 2008, 2:48:00 AM2/1/08
to caml-list List

Le 1 févr. 08 à 08:27, Michaël Grünewald a écrit :

> Bünzli Daniel <daniel....@erratique.ch> writes:
>
>>> time jot 1000000 | ./in.native
>>
>> real 0m4.814s
>> user 0m4.371s
>> sys 0m0.218s
>
> You are timing `jot' aren't you?

Not really, only half of the time. See at the end of this email.

> The recommendation you proposed would be even more useful if it
> emphasizes when ``Exceptionless error management'' is adequate.

Right. I'll try to do something along this line. I'll will highlight
in the suggestions part (which is not part of the recommendation) that
in Unix-like cases the recommendation should maybe not be followed.

Best,

Daniel


> jot 1000000 > lines.txt

> time ./in.native < lines.txt

real 0m6.093s
user 0m2.132s
sys 0m0.219s

> time ./inb.native < lines.txt

real 0m6.827s
user 0m2.117s
sys 0m0.218s

David Teller

unread,
Feb 1, 2008, 3:35:23 AM2/1/08
to Bünzli Daniel, caml-list List
Hi list,

If this is of any interest for the discussion, I have just finished
putting together a first prototype of fast exception monad. There's room
for at least one huge optimization, but the overall idea shouldn't
change. I've documented it somewhat here [1]. If there's interest, I
guess I could try and reimplement some core modules to make use of this
facility, as a manner of determining its usefulness. Or, rather, to add
a compatibility layer for some core modules.

Cheers,
David

[1]
http://dutherenverseauborddelatable.wordpress.com/downloads/exception-monads-for-ocaml/

On Thu, 2008-01-31 at 09:55 +0100, Bünzli Daniel wrote:
> Hello,
>
> Based on discussions and the resulting consensus that occured more
> than once on this list. I wrote a recommendation on how modules should
> perform error management. It is available here :
>
> http://cocan.org/osr/exceptionless_error_management
>
> Best,
>
> Daniel

--

David Teller
Security of Distributed Systems
http://www.univ-orleans.fr/lifo/Members/David.Teller
Angry researcher: French Universities need reforms, but the LRU act brings liquidations.

_______________________________________________

Till Varoquaux

unread,
Feb 1, 2008, 5:50:45 AM2/1/08
to Bünzli Daniel, caml-list List
I am still very ambivalent:

While I think having clearer signatures is great, I am still wary
that this solution might prove too much of a constraint in some cases.
I still think exceptions shouldn't be shunned so quickly. As their
name clearly stipulates they should be used to handle exceptional
cases, and sometimes the nature of what's an exceptional case is
better left to the programmer's judgment (e.g. find). I believe that,
when needed, both functions should be exposed in the interface (e.g.
find and find_exn, or find and find_opt).

Exceptions are an amazing tool to handle exceptional cases because
they unwind stack the automatically. This means that you don't have
to constantly thing about propagating the errors manually. A monad
can, of course, help with this, but there is a cost in code clarity to
consider. They can also help you identify the source of issues by
giving a back trace that is ultimately printed at the root of your
program (very hard to achieve without exceptions).

Choosing whether an error case should be treated as an exception is a
matter which, IMHO, does not have a general answer and depends on:
- How often the errors can occur and how they can be treated
- How safe you require your API to be. If you run with tight security
restrictions you will probably be more likely to take the
exception-less route. Note that taking a radical stance (like the one
advocated in your OSR) might make your programs a lot more verbose
and/or harder to grasp. Simplicity also leads to better security.
- Personal tastes.

I am also not very enthused by the use of the polymorphic variant
versus a Haskell like [Left | Right] variant. I think the former can
lead to hard to track errors (excerpt from the manual: "Beware also
that some idioms make trivial errors very hard to find.") bringing an
illusion of safety rather than safety itself. The latter also
separates in clearer way the error cases from the success cases.

Just my 0.02 (euro) cents.

Till

--
http://till-varoquaux.blogspot.com/

Bünzli Daniel

unread,
Feb 1, 2008, 6:32:31 AM2/1/08
to caml-list List

Le 1 févr. 08 à 11:50, Till Varoquaux a écrit :

> While I think having clearer signatures is great, I am still wary
> that this solution might prove too much of a constraint in some cases.
> I still think exceptions shouldn't be shunned so quickly. As their
> name clearly stipulates they should be used to handle exceptional
> cases, and sometimes the nature of what's an exceptional case is
> better left to the programmer's judgment (e.g. find). I believe that,
> when needed, both functions should be exposed in the interface (e.g.
> find and find_exn, or find and find_opt).

I stand behind the one liner that will make the error implicit if you
want. Explicit first, implicit if you want to take the risk.
Personnally I wouldn't make it implicit unless it is for an "assert
false", but this is a matter of philosophy.

> Exceptions are an amazing tool to handle exceptional cases because
> they unwind stack the automatically. This means that you don't have
> to constantly thing about propagating the errors manually.

I have nothing against exceptions per se. I also use them in my own
programs. But I don't want libraries to force me to use them. Recall
that the recommendation says nothing about client code. You can
perfectly define your own exceptions to propagate the error up.

> I am also not very enthused by the use of the polymorphic variant
> versus a Haskell like [Left | Right] variant. I think the former can
> lead to hard to track errors (excerpt from the manual: "Beware also
> that some idioms make trivial errors very hard to find.") bringing an
> illusion of safety rather than safety itself. The latter also
> separates in clearer way the error cases from the success cases.

On the safety bit I think this is less true if you actually take care
to close your variants. However I tend to agree on the clearer
separation of cases. I added a request for comments on this at the end
of the OSR page to standardize on [ `Value of ... | `Error of ... ]
instead of [ `Value of ... | .... ].

Best,

Daniel

Yaron Minsky

unread,
Feb 1, 2008, 7:19:30 AM2/1/08
to David Teller, Bünzli Daniel, caml-list List
We've come to be pretty leery of exceptions at Jane Street, and we've had
some experience trying to control their use Some quick observations on the
current proposal:

1. Banning exceptions outright seems too strict. Our tendency has
been to have two versions of the function, e.g., Map.find and
Map.find_exn. (Our previous approach would have been to use
Map.find_opt for the one that returns and option and Map.find for the
exception-throwing one, but we're slowly migrating to the first proposal.)
The goal is to make the user aware that an exception is reasonably likely,
rather than ban exceptions.
2. We've played around with monadic error systems that combine errors
into a big polymorphic variant. The key practical problem we hit were
obscenely complicated error messages. Still, in some cases, monads are very
nice.
3. Having a variant type other than option so you can specify various
different errors (ours is Ok of 'a | Error of 'b) is a good idea. And I
think it's common enough that you should define it in your core library as
an ordinary variant and make it available everywhere.

y

Vincent Hanquez

unread,
Feb 1, 2008, 11:00:09 AM2/1/08
to Bünzli Daniel, caml-list List
On Fri, Feb 01, 2008 at 12:31:52PM +0100, Bünzli Daniel wrote:
> On the safety bit I think this is less true if you actually take care to
> close your variants. However I tend to agree on the clearer separation of
> cases. I added a request for comments on this at the end of the OSR page to
> standardize on [ `Value of ... | `Error of ... ] instead of [ `Value of ...
> | .... ].

but what's wrong with the plain [ Value of ... | Error of ... ] variant
instead of polymorphic variant ?

--
Vincent Hanquez

David Allsopp

unread,
Feb 1, 2008, 11:04:46 AM2/1/08
to caml-list List
> > While I think having clearer signatures is great, I am still wary
> > that this solution might prove too much of a constraint in some cases.
> > I still think exceptions shouldn't be shunned so quickly. As their
> > name clearly stipulates they should be used to handle exceptional
> > cases, and sometimes the nature of what's an exceptional case is
> > better left to the programmer's judgment (e.g. find). I believe that,
> > when needed, both functions should be exposed in the interface (e.g.
> > find and find_exn, or find and find_opt).
>
> I stand behind the one liner that will make the error implicit if you
> want. Explicit first, implicit if you want to take the risk.
> Personnally I wouldn't make it implicit unless it is for an "assert
> false", but this is a matter of philosophy.

Making the error explicit (i.e. using match) forces a decision on the client
of a library - and that breaks the golden rule of library design because
it's none of your business (as a library author) to force any decision on
the end-user of your library that is not necessary to maintain correct
operation of your library.

> > Exceptions are an amazing tool to handle exceptional cases because
> > they unwind stack the automatically. This means that you don't have
> > to constantly thing about propagating the errors manually.
>
> I have nothing against exceptions per se. I also use them in my own
> programs. But I don't want libraries to force me to use them. Recall
> that the recommendation says nothing about client code. You can
> perfectly define your own exceptions to propagate the error up.

But your proposal forces me to *use* 'a option when I want to *ignore*
exceptions. If I've got a map and, by proof, I know that I'll never do a
find on it that would raise Not_found then I sure don't want to wrap every
Map.find with either Option.get or a match clause.

I heartily approve of the notion of exceptions for exceptional cases as a
rule of thumb[*] - for example, many of the exceptions used in the Unix
module "should" definitely be 'a option (you should never perform I/O
without error checking, so 'a option - or, as I would use, ('a, 'b) result
is appropriate).

Furthermore, in the case of functions such as Map.find, the library author
*cannot* know whether failure to find an item in the map is exceptional or
not - and for that reason you cannot mandate 'a option as a return value -
the only real solution is to provide both functions and allow the client to
decide whether failure to find an item is exception or, failing that,
provide the path with the easiest wrapping - and I'm afraid that's
unarguably the exception route.

Incidentally, there's no need to benchmark to discover whether 'a option
wrapping is slower - it must be because it involves an extra allocation over
using exceptions. However, this difference is probably not that great (and
there are more possible compiler optimisations - though not necessarily
exploited by OCaml - that become possible if you don't use exceptions).


David


[*] Although IMHO "exceptions for exceptional circumstances" is a bit of a
beginners' mantra. Exceptions should be used in situations where you need to
unwind the stack - exceptional circumstances are one instance of this.
Another, equally valid, use of exceptions is in search algorithms - you may
be several stack levels down before discovering that a certain branch
decision was incorrect and using exception programming to unwind this is
much neater than passing values around.

Bünzli Daniel

unread,
Feb 1, 2008, 1:37:55 PM2/1/08
to Vincent Hanquez, caml-list List

Le 1 févr. 08 à 16:59, Vincent Hanquez a écrit :

> but what's wrong with the plain [ Value of ... | Error of ... ]
> variant
> instead of polymorphic variant ?

If it is not defined in Pervasives you have to redefine in it every
module that follows the recommendation and prefix with module names on
matching. Annoying.

Now if you know how to get something new accepted in Pervasives I have
no problem with a plain variant type.

Best,

Daniel

Vincent Hanquez

unread,
Feb 1, 2008, 2:43:34 PM2/1/08
to Bünzli Daniel, caml-list List
On Fri, Feb 01, 2008 at 07:37:45PM +0100, Bünzli Daniel wrote:
> If it is not defined in Pervasives you have to redefine in it every module
> that follows the recommendation and prefix with module names on matching.
> Annoying.

Yes, that's annoying. but polymorphic variant got their own annoyance as well.

> Now if you know how to get something new accepted in Pervasives I have no
> problem with a plain variant type.

last time i checked, Pervasives is LGPL+static exception.
we can add anything we need in Pervasives.

--
Vincent Hanquez

David Teller

unread,
Feb 5, 2008, 5:01:16 AM2/5/08
to Bünzli Daniel, caml-list List
Ok everyone,

The debate on this subject seems to have calmed down. A tentative set
of recommendations have been put together on the wiki [1] by some of the
participants. It looks coherent and well-written. If nothing springs
back to the front of the scene within a few days, I'll post this again
to the mailing-list as a call for votes.

Cheers,
David

[1] http://cocan.org/osr/exceptionless_error_management


On Thu, 2008-01-31 at 09:55 +0100, Bünzli Daniel wrote:
> Hello,
>
> Based on discussions and the resulting consensus that occured more
> than once on this list. I wrote a recommendation on how modules should
> perform error management. It is available here :
>
> http://cocan.org/osr/exceptionless_error_management
>
> Best,
>
> Daniel
>

> _______________________________________________
> Caml-list mailing list. Subscription management:
> http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list
> Archives: http://caml.inria.fr
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs
>

--
David Teller
Security of Distributed Systems
http://www.univ-orleans.fr/lifo/Members/David.Teller
Angry researcher: French Universities need reforms, but the LRU act brings liquidations.

_______________________________________________

Vincent Hanquez

unread,
Feb 5, 2008, 5:12:42 AM2/5/08
to David Teller, Bünzli Daniel, caml-list List
On Tue, Feb 05, 2008 at 11:00:44AM +0100, David Teller wrote:
> The debate on this subject seems to have calmed down. A tentative set
> of recommendations have been put together on the wiki [1] by some of the
> participants. It looks coherent and well-written. If nothing springs
> back to the front of the scene within a few days, I'll post this again
> to the mailing-list as a call for votes.

looking at the page, I find a proposal (beginning), and a lots of people
disaggreeing at the end.

I can see lots of people are concerned by using polymorphic variant (and
I certainly agree with those), and people asking for a monadic approch
(haskell's either like). I can see anything change in the proposal
regarding people's concerned, so what are you going to call for a vote ?

--
Vincent Hanquez

Bünzli Daniel

unread,
Feb 5, 2008, 5:26:45 AM2/5/08
to caml-list List

Le 5 févr. 08 à 11:12, Vincent Hanquez a écrit :

> looking at the page, I find a proposal (beginning), and a lots of
> people
> disaggreeing at the end.

I agree with this I think it should be moved to a "rejected proposals"
section. When I find some time I will add a preamble explaining why it
was rejected.

> I can see lots of people are concerned by using polymorphic variant
> (and
> I certainly agree with those), and people asking for a monadic approch
> (haskell's either like).

Still I think this is a little bit sad. Using polymorphic variants
isn't that bad at all as long as we just use the following _closed_
type [ `Value of ... | `Error of ... ]. This would allow us to move
forward despite that fact that Pervasives is frozen (and no I'm not
interested in forking it). Sure we can have monadic stuff, new types,
new infrastructure etc. but I try to design solely within the
constraint of the ocaml system as it stands because I know that's
something has been there for more than 10 years and continues to be
maintained. Polymorphic variants have the advantage to allow us to
standardize across modules without needing changes to the ocaml system.

Best,

Daniel

Vincent Hanquez

unread,
Feb 5, 2008, 6:07:17 AM2/5/08
to Bünzli Daniel, caml-list List
On Tue, Feb 05, 2008 at 11:26:00AM +0100, Bünzli Daniel wrote:
>> I can see lots of people are concerned by using polymorphic variant (and
>> I certainly agree with those), and people asking for a monadic approch
>> (haskell's either like).
>
> Still I think this is a little bit sad. Using polymorphic variants isn't
> that bad at all as long as we just use the following _closed_ type [ `Value
> of ... | `Error of ... ].

imho, polymorphic variants ""weaken"" the type system and are a royal PITA
when something is wrong in the code and you need to find it.

for example something plain _stupid_ as:
returning or matching `VAlue instead of `Value

since it will be created "dynamicly" (you don't need to define it), ocaml
will be perfectly happy to return/match it, but you'll have a "weird" error
somewhere else:

# let f x : [`Error | `Value of int ] = if x <= 0 then `Error else `Value x;;
val f : int -> [ `Error | `Value of int ] = <fun>

# let g y = match f y with `Error -> "error" | `VAlue x -> "value"

This pattern matches values of type [< `Error ]
but is here used to match values of type [ `Error | `Value of int ]
The first variant type does not allow tag(s) `Value

with normal variant i'll have a nice error:

Unbound constructor VAlue

And that's just one thing on the list of 'against' polymorphic variants;
i'm sure people have their own set of bad experiences with polymorphic
variants.

> This would allow us to move forward despite that
> fact that Pervasives is frozen (and no I'm not interested in forking it).

This is not "forking", for me that's patching.
We _can_ provide the same interface+improvements (no breaking of
previous programs) easily _without_ forking with minimal intrusion as a
patchqueue on top of ocaml stdlib. the compiler might be forbidden
fruit (license, INRIA expertise, etc) and that's probably a bad idea to
fork it anyway, but stdlib should be where we could add major improvement
to the language (a rich and nice stdlib just like all other languages).

Also a proposal that define exceptionless without having the core
library like Hashtbl, Map, Set, List, conforms to this "standard" is
just bound to failure. Either they are modified (bad idea for
compability), Copied+forked into another module (nobody going to use it
+ you're forking some of the stdlib) or the proposal is moot.

> Sure we can have monadic stuff, new types, new infrastructure etc. but I
> try to design solely within the constraint of the ocaml system as it stands
> because I know that's something has been there for more than 10 years and
> continues to be maintained.
> Polymorphic variants have the advantage to
> allow us to standardize across modules without needing changes to the ocaml
> system.

they might be standard in ocaml (as in available through the compiler),
but not much library use them.

people should have that in mind when trying to propose them as an OSR;
lots of people are not confortable with them imo.

--
Vincent Hanquez

Frédéric van der Plancke

unread,
Feb 5, 2008, 6:39:30 AM2/5/08
to caml...@inria.fr
Bünzli Daniel wrote:
> Still I think this is a little bit sad. Using polymorphic variants
> isn't that bad at all as long as we just use the following _closed_
> type [ `Value of ... | `Error of ... ]. This would allow us to move
> forward despite that fact that Pervasives is frozen (and no I'm not
> interested in forking it).
But Pervasives is not *that* frozen. If I understand things well, the
main concern of Inria is having to maintain the would-be additions and
changes to Pervasives. An Ok | Error variant doesn't look like it will
entail heavy maintenance charges. So they may accept to add it.

(I don't think there are backwards-compatibility problems either as
user-defined Ok|Error would hide a Pervasives-defined one in all
contexts I can think of.)

Frédéric

David Teller

unread,
Feb 5, 2008, 9:13:36 AM2/5/08
to Vincent Hanquez, Bünzli Daniel, caml-list List
On Tue, 2008-02-05 at 11:12 +0100, Vincent Hanquez wrote:
> On Tue, Feb 05, 2008 at 11:00:44AM +0100, David Teller wrote:
> > The debate on this subject seems to have calmed down. A tentative set
> > of recommendations have been put together on the wiki [1] by some of the
> > participants. It looks coherent and well-written. If nothing springs
> > back to the front of the scene within a few days, I'll post this again
> > to the mailing-list as a call for votes.
>
> looking at the page, I find a proposal (beginning), and a lots of people
> disaggreeing at the end.
>
> I can see lots of people are concerned by using polymorphic variant (and
> I certainly agree with those), and people asking for a monadic approch
> (haskell's either like). I can see anything change in the proposal
> regarding people's concerned, so what are you going to call for a vote ?

Ok, my bad, I guess I misunderstood silence for consensus.

In this case, let me try and sum up the discussion. I'll use the term
"exception" for OCaml's built-in exception system.

1) This recommendation applies to functional libraries, by opposition to
essentially imperative libraries such as Unix.
[ I believe everybody agrees with that ]

2) This recommendation only applies to the interface of libraries,
rather than their non-observable implementation or their use by clients.
[ I believe everybody agrees with that ]

3) Exceptions passing library barriers should be reserved for programmer
errors (i.e. invalid arguments and impossible-to-reach states) rather
than previsible errors (e.g. sometimes, a key does not map to a value).
We already have standard names for such exceptions.
[ I have the impression nobody disagrees with that, either ]

4) At hand, to provide non-exception error management, we have the
following tools:
* 'a option
* something such as ('a, 'b) either -- exact name and constructors
remain to be decided, but the general idea is there -- which may serve,
if necessary, as the base for error monads
* polymorphic variants
* an implementation of error monad based on exceptions, which may be
trivially degraded to ('a, 'b) either whenever necessary.

(I'm discounting my own suggestion for functor-based error reporting as
I haven't had time to work on that, plus it would actually solve a
different problem)

[ Unless I'm mistaken, these are the only tools suggested so far ]

5) We may need to differentiate functions which raise exceptions.
Someone on this thread suggested adding suffix "_exn" to the name of
such functions. Another possibility would be to define a type such as
type 'a raising = 'a
to document the fact that a function may raise an exception.

[ No discussion on this yet ]


6) Assuming that no other tools come into the discussion, we need to
determine which tools are best for which situation.

There seems to be an agreement that polymorphic variant are too fragile
for general use. This leaves us essentially
* ('a,'b) either (of which 'a option is just a special case)
* the exception-based error monad, with the ability to produce results
of type 'a or errors of type 'b .

These cases are fundamentally equivalent, each may be used to build the
other and result in essentially the same type signature.

[ No discussion on this yet ]


7) Assuming that we use one of these two possibilities, we need to
decide what to put in the 'b.

Here, we may decide to:
* either define module-specific types ;
* or use polymorphic variants.

Again, the general consensus seems to be that polymorphic variants are
too risky. On the other hand, we may also decide not to specify at all
what to put in 'b and to let each developer decide for their own
library.

[ No discussion on this yet ]


As a test, I've adapted the interface of the List module.
* I've included the interface for both possibilities
* I've postfixing exception-raising functions with "_exn"
* I've defined module-specific sum types, some of which could presumably
be moved to Pervasives.

You may find the result here:
http://www.univ-orleans.fr/lifo/Members/David.Teller/software/exceptions/list_with_errors.mli

Cheers,
David

--
David Teller
Security of Distributed Systems
http://www.univ-orleans.fr/lifo/Members/David.Teller
Angry researcher: French Universities need reforms, but the LRU act brings liquidations.

_______________________________________________

Jon Harrop

unread,
Feb 5, 2008, 9:14:02 AM2/5/08
to caml...@yquem.inria.fr, Bünzli Daniel, caml-list List
On Tuesday 05 February 2008 11:06:41 Vincent Hanquez wrote:
> This is not "forking", for me that's patching.
> We _can_ provide the same interface+improvements (no breaking of
> previous programs) easily _without_ forking with minimal intrusion as a
> patchqueue on top of ocaml stdlib. the compiler might be forbidden
> fruit (license, INRIA expertise, etc) and that's probably a bad idea to
> fork it anyway, but stdlib should be where we could add major improvement
> to the language (a rich and nice stdlib just like all other languages).
>
> Also a proposal that define exceptionless without having the core
> library like Hashtbl, Map, Set, List, conforms to this "standard" is
> just bound to failure. Either they are modified (bad idea for
> compability), Copied+forked into another module (nobody going to use it
> + you're forking some of the stdlib) or the proposal is moot.

Exactly. Whatever everyone wants to call it, the stdlib desperately needs
improving and tacking on more third party libraries (ExtLib, AnnexLib,
PagodaCore, Baird, ...) is not the way to do it.

I personally want to see lots of simple, minimal improvements to the stdlib
but I'm afraid that lots of other people are going to want to push their
untested Obj-riddled codebases into any improvement, which scares me.

> > Sure we can have monadic stuff, new types, new infrastructure etc. but I
> > try to design solely within the constraint of the ocaml system as it
> > stands because I know that's something has been there for more than 10
> > years and continues to be maintained.
> > Polymorphic variants have the advantage to
> > allow us to standardize across modules without needing changes to the
> > ocaml system.
>
> they might be standard in ocaml (as in available through the compiler),
> but not much library use them.

Jacques Garrigue's libraries (e.g. LablGL) do and they work very well.

This is one area where OCaml is much nicer than F# (inherited from .NET). For
example, in F# you write CullFace.Both or ControlStyles.DoubleBuffer but in
OCaml you just write `Both or `DoubleBuffer.

> people should have that in mind when trying to propose them as an OSR;
> lots of people are not confortable with them imo.

I would recommend avoiding recursive polymorphic types in library APIs but I
have no problems with small, simple, non-recursive definitions like [ `Value
of 'a | `Exception of 'b ].

--
Dr Jon D Harrop, Flying Frog Consultancy Ltd.
http://www.ffconsultancy.com/products/?e

_______________________________________________

Michaël Grünewald

unread,
Feb 6, 2008, 3:46:29 AM2/6/08
to Bünzli Daniel, caml-list List
Bünzli Daniel <daniel....@erratique.ch> writes:

> Le 5 févr. 08 à 11:12, Vincent Hanquez a écrit :
>
>> looking at the page, I find a proposal (beginning), and a lots of
>> people
>> disaggreeing at the end.
>
> I agree with this I think it should be moved to a "rejected proposals"
> section. When I find some time I will add a preamble explaining why it
> was rejected.

Although it did not meet a consensus, the proposal might still be
useful. Developpers should be able to claim at the beginning of a file
or in their projet's documentation:

``We adhere to conventions described in PAPER, regarding
exceptionless error reporting.''

The benefit from doing so, is that it helps having coherent code
(users can report that parts of the code break proposal, etc.).

So ``rejected proposals'' sounds a bit pessimistic to me, we could
have four sections (or even more):
1. proposals (for discussion);
2. recommendations (proposals that met a large success);
3. options (proposals that did not met a large success, or are not
intended for large audience, but are still a useful reference
because they are adhered to in some code);
4. attic (proposals that do not fit in the first three, maybe because
they have been rejected).

(The vote mechanism should then only apply to publications in 2.)

By the way the title of your proposal is informative about the problem
you are giving a possible answer, but it do not give a word about the
solution. I think it should, beacuse discussion on the page show that
there exist(?) other strategies than the one you describe.
--
All the best,
Michaël

Bünzli Daniel

unread,
Feb 8, 2008, 8:09:48 AM2/8/08
to caml-list List
Le 6 févr. 08 à 09:45, Michaël Grünewald a écrit :

> Although it did not meet a consensus, the proposal might still be
> useful. Developpers should be able to claim at the beginning of a file
> or in their projet's documentation:
>
> ``We adhere to conventions described in PAPER, regarding
> exceptionless error reporting.''

Yes. As I said earlier, I see this recommendation more as a design
pattern than a recommendation.

Since people still want to use exceptions in some cases, I narrowed
the scope of the recommendation from modules to functional values.

Best,

Daniel

David Teller

unread,
Feb 11, 2008, 3:13:38 AM2/11/08
to Bünzli Daniel, caml-list List
I've re-read the candidate and I have one suggestion: at the moment, you
want to standardize both `Success and `Error and use `Error to carry
further results. Why not trim-down slightly this recommandation and
standardize only `Success (or the more concise `Ok, as suggested by
Yaron) ?

This would let us have

val find: ('a -> bool) -> 'a list -> [`Ok of 'a | `Not_found]
val open_file : string -> [`Ok of file_handler | `No_such_file |
`No_authorization]

etc.

I believe this would make code more readable.

Cheers,
David

On Thu, 2008-01-31 at 09:55 +0100, Bünzli Daniel wrote:
> Hello,
>
> Based on discussions and the resulting consensus that occured more
> than once on this list. I wrote a recommendation on how modules should
> perform error management. It is available here :
>
> http://cocan.org/osr/exceptionless_error_management
>

> Best,
>
> Daniel
>
> _______________________________________________
> Caml-list mailing list. Subscription management:
> http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list
> Archives: http://caml.inria.fr
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs
>

--
David Teller
Security of Distributed Systems
http://www.univ-orleans.fr/lifo/Members/David.Teller
Angry researcher: French Universities need reforms, but the LRU act brings liquidations.

_______________________________________________

Bünzli Daniel

unread,
Feb 11, 2008, 4:09:32 AM2/11/08
to David Teller, caml-list List

Le 11 févr. 08 à 09:12, David Teller a écrit :

> I've re-read the candidate and I have one suggestion: at the moment,
> you
> want to standardize both `Success and `Error and use `Error to carry
> further results. Why not trim-down slightly this recommandation and
> standardize only `Success (or the more concise `Ok, as suggested by
> Yaron) ?

Actually the initial version did exactly this. I changed it because
some commented that they wanted a clear case for the error. But if
most agree with you I'm willing to change it back.

0 new messages