> I think we still need an additional "exn_of_error" FYI, Core provides `ok_exn : 'a Or_error.t -> 'a`, which I find very useful.
On Wed, Feb 4, 2015 at 9:09 AM, Thomas Leonard <[email protected]> wrote: > On 4 February 2015 at 13:01, Leo White <[email protected]> wrote: > > Hi all, > > > > As this discussion keeps on running, I thought I would add a couple of > > thoughts: > > > > - The problem with using exceptions for returning errors is that it > > becomes very difficult to distinguish between a dynamic error that is > > part of normal use (e.g. this network connection failed) from a > > programming error (e.g. Not_found accidently leaked from a use of > > List.find, an assert false triggering). This makes it hard to produce > > the correct behaviour in all cases: there are applications for which > > it would be better to kill everyting on detecting a programmer error > > rather than risk continuing in an unstable state. > > I don't think exception vs error code is a reliable way to divide > these cases up. Having your FS return `Block_error indicates a serious > problem that might require terminating the unikernel, whereas getting > a Division_by_zero exception from your HTTP handler is likely fairly > harmless. Whether an exception/error is serious depends more on the > importance of the thing that raised it. > > Consider the case of a filesystem that reads a corrupted disk and > throws an exception (e.g. an assert fails). This is probably the most > extreme case where you'd want to abort. Should it terminate the > unikernel? > > It depends what the disk is being used for. If it's the main hard-disk > then possibly. If it's some removable media the user has just inserted > then certainly not. > > A good principle here is that a broken component should only be able > to harm itself. If a filesystem fails to handle a corrupted disk > correctly then it may further corrupt that disk, but it should not > abort the unikernel (and thus possibly corrupt other disks in the > middle of being written). > > In this case, we can imagine a fail-safe FS functor that wraps all the > calls in the FS API so that if any one of them throws an unexpected > exception then it unplugs the underlying block device. No need to kill > everything. > > > - The problem with using Lwt.t as your error monad is that it becomes > > difficult to distiunguish between synchronous things that may return > > errors, asynchronous things that may return errors, and asynchornous > > things that should not return errors. It also seems tied up with the > > exception mechanism, which leads to the same problem as my previous > > point. > > > > Personally, I would probably suggest that all Mirage modules/module > > types include in their signatures: > > > > type error > > > > val pp_error : formatter -> error -> string > > > > For cases where an error can reasonably be matched on and handled > > specially, this should be exposed in the signature: > > > > type error = private [> `Foo of foo | `Bar of bar] > > Aha! I knew you'd know a trick to make this work! > > However, different functions return different sets of errors. For > example, BLOCK.read shouldn't return `Is_read_only. Can we handle > that? > > > val pp_error : formatter -> error -> string > > I've added some error_message (error -> string) functions, but this > might be better. I haven't used formatters much, so don't have an > opinion here. > > But I think we still need an additional "exn_of_error" here because > whether something is a "dynamic" (expected) error or a bug changes as > the error is propagated. > > For example, an XML parser probably regards a malformed document as a > dynamic error (`Malformed of malformed), which its caller may want to > handle. But if the caller is trying to load its configuration file > from a crunch FS, then malformed XML is a programming bug and should > be thrown as an exception. > > > 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. The various "finally" functions for resources > > should catch and reraise them, so that they can reach the outermost > > scope which knows how best to deal with a programming error in the > > particular application. The same goes for binding on an Lwt thread which > > raised an exception or failed: it should cause an exception to be raised > > to reach the outermost level. > > Yes, I think it does this. > > > The aim here is still to take the erlang-style kill the component that > > failed and try again approach, but to ensure that there are two distinct > > return paths for regular errors and programming errors. The secondary > > aim is to have module signatures which give a clear indication of > > intended use. > > > > Regards, > > > > Leo > > > > -- > Dr Thomas Leonard http://0install.net/ > GPG: 9242 9807 C985 3C07 44A6 8B9A AE07 8280 59A5 3CC1 > GPG: DA98 25AE CAD0 8975 7CDA BD8E 0713 3F96 CA74 D8BA > > _______________________________________________ > MirageOS-devel mailing list > [email protected] > http://lists.xenproject.org/cgi-bin/mailman/listinfo/mirageos-devel >
_______________________________________________ MirageOS-devel mailing list [email protected] http://lists.xenproject.org/cgi-bin/mailman/listinfo/mirageos-devel
