[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [MirageOS-devel] Error handling in Mirage - request for comments!



>
> I've had a go at writing this proposal up ("system 5"):
>
>   https://github.com/talex5/mirage-www/blob/errors/tmpl/wiki/error-handling.md
>
> I think someone who wants to advocate this style should check it /
> change it. It probably needs some worked examples showing it being
> used too.
>

The interface given for "system 5" is not comparable for the one given
for "system 4", giving a false sense of verbosity to the "system 5"
example. The equivalent to the "system 4" example, would just be:

  type error

  val pp_error : formatter -> error -> string

  val read :
    t -> int64 -> page_aligned_buffer list ->
    (int, error) result

Constructors of `error` should only be exposed if they are intended to
be matched on. So if `read` has a kind of error ``Foo` which was
intended to be matched on then the "system 5" approach would be:

  type error = private [> `Foo]

  val pp_error : formatter -> error -> string

  val read :
    t -> int64 -> page_aligned_buffer list ->
    (int, error) result

whilst "system 4" would probably look like:

  exception Foo

  val read :
    t -> int64 -> page_aligned_buffer list -> int

The splitting of `error` up into `write_error` and `read_error` is only
needed if you have an interface with different sets of matchable
errors. So if you had a `read` function with a matchable error ``Foo`
and a `write` function with matchable error ``Bar`, then you would get
the following:

  type read_error = private [> `Foo]
  type write_error = private [> `Bar]

  type error
  val pp_error : formatter -> error -> string

  val write_error : write_error -> error
  val read_error : read_error -> error

  val read :
    t -> int64 -> page_aligned_buffer list ->
    (int, read_error) result

  val write :
    t -> int64 -> page_aligned_buffer list ->
    (int, write_error) result

whilst "system 4" would probably look like:

  exception Foo

  exception Bar

  val read :
    t -> int64 -> page_aligned_buffer list -> int

  val write :
    t -> int64 -> page_aligned_buffer list -> int

A key benefit of "system 5" is that the signature contains all the
interesting information about `read` and `write`. It clearly states that
they are expected to fail sometimes, and that the only errors on which it is
reasonable to behave specially are a `Foo` returned by `read` or a `Bar`
returned by `write`. `System 4` does not convey any of this information.

>> Some nice combinators should be provided for using ('a, 'b) Result.t and
>> ('a, 'b) Result.t Lwt. and for lifting an ('a, Foo.error) Result.t into
>> an ('a, Bar.error) Result.t.
>>
>> Exceptions that escape their intended scope, should always be treated as
>> a programming error.
>
> Yes, by definition. But what should the "intended scope" be, and how
> do we ensure the producer and consumers of the exception agree?

Exceptions should basically never delibrately cross module
boundaries. They should certainly not be part of the exposed API of a
library. By "exceptions that escape their scope" I essentially meant
exceptions used in third-party APIs (e.g. Not_found produced by
List.find), these should always be caught as early as possible and
handled appropriately.

Occasionally it is reasonable for an exception like "Invalid_argument"
to be part of a library API, where the exception is not intended to be
caught but merely to indicate that there has been a programmer error.

It is also reasonable to use exceptions locally for control-flow, but
again these should not escape into the visible API.

Regards,

Leo

_______________________________________________
MirageOS-devel mailing list
MirageOS-devel@xxxxxxxxxxxxxxxxxxxx
http://lists.xenproject.org/cgi-bin/mailman/listinfo/mirageos-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.