source: doc/working/exception/design.txt @ 88e0080

Last change on this file since 88e0080 was 88e0080, checked in by Andrew Beach <ajbeach@…>, 5 years ago

Added a file with notes about the new exception design.

  • Property mode set to 100644
File size: 6.2 KB
1Design of Exceptions and EHM in Cforall:
3Currently this is a combination of ideas and big questions that still have to
4be addressed. It also includes some other error handling options, how they
5interact and compare to exceptions.
8What is an Exception:
10In other words what do we throw? What is matched against, how does it carry
11data with it? A very important question that has not been answered.
13Option 1: Strutures
15Considering the current state of Cforall the most natural form of the
16exception would likely be a struture, implementing a trait repersenting the
17minimum features of an exception. This has many advantages, including arbitray
18fields, some polymorphism and it matches exceptations of many current systems.
20The main issue with this is matching, without OOP inheritance there is no
21exception hierarchy. Meaning all handling has to happen on the exact exception
22without the ease of grouping parents. There are several ways to attempt to
23recover this.
25The first is with conditional matching (a check after the type has been
26matched) which allows for matching particular values of a known type. However
27this does not dynamically expand and requires an extra step (as opposed to
28mearly allowing one). I would not recomend this as the primary method.
30Second is to try and use type casts/conversions to create an implicate
31hierachy, so that a catch clause catches anything of the given type or
32anything that converts to the given type.
34Plan9 (from what I know of it) would be a powerful tool here. Even with it,
35creating a hierarchy of types at runtime might be too expencive. Esecially
36since they are less likely to be tree like at that point.
38Option 2: Tags
40The other option is to introduce a new construct into the language. A tag
41repersents a type of exception, it is not a structure or variable (or even
42a normal type). It may be usable in some of those contexts.
44Tags can declare an existing tag as its parent. Tags can be caught by handlers
45that catch their parents. (There is a single base_exception that all other
46exceptions are children of eventually.) This allows for grouping of exceptions
47that repersent similar errors.
49Tags should also have some assotiated data, where and on what did the error
50occur. Keeping with the otherness of exception tags and allowing them to be
51expanded, using a parameter list. Each exception can have a list of paramters
52given to it on a throw. Each tag would have a declared list of parameters
53(which could be treated more like a set of fields as well). Child tags must
54use their parent's list as a prefix to their own, so that the parameters can
55be accessed when the child tag is matched against the parent.
57Option N: ...
59This list is not complete.
62Seperating Termination and Resumption:
64Differentating the types of exceptions based on exception would be hard with
65exceptions as structures. It is possible with exceptions as tags by having
66two base exceptions, one for each type of throw. However recompining them
67to dual types would be harder.
69Reguardless, using different keywords would also be useful for clarity, even
70if it does not add functality. Using the C++ like keywords would be a good
71base. Resumption exceptions could use a different set (ex. raise->handle) or
72use resume as a qualifier on the existing statements.
75Conditional Matching:
77A possible useful feature, it allows for arbitrary checks on a catch block
78instead of merely matching a type. However there are few use cases that
79cannot be avoided with proper use of a type hierarchy, and this shrinks even
80further with a good use of re-throws.
82Also it assumes one sweep, that might also be a problem. But might also give
83it an advantage over re-throws.
86Alternatives: Implicate Handlers & Return Unions
88Both act as a kind of local version of an exception. Implicate handlers act as
89resumption exceptions and return unions like termination exceptions. By local
90I mean they work well at one or two levels of calls, but do not cover N levels
91as cleanly.
93Implicate handles require a growing number of function pointers (which should
94not be used very often) to be passed to functions, creating and additional
95preformance cost. Return unions have to be checked and handled at every level,
96which has some preformance cost, but also can significantly clutter code.
97Proper tools can help with the latter.
99However, they may work better at that local level as they do not require stack
100walking or unwinding. In addition they are closer to regular control flow and
101are easier to staticly check. So even if they can't replace exceptions
102(directly) they may still be worth using together.
104For instance, consider the Python iterator interface. It uses a single
105function, __next__, to access the next value and to signal the end of the
106sequence. If a value is returned, it is the next value, if the StopIteration
107exception is thrown the sequence has finished.
109However almost every use of an iterator will add a try-except block around the
110call site (possibly through for or next) to catch and handle the exception
111immediately, ignoring the advantages of more distant exception handling.
113In this case it may be cleaner to use a Maybe for both cases (as in Rust)
114which gives similar results without having to jump to the exception handler.
115This will likely handle the error case more efficiently and the success case a
116bit less so.
118It also mixes the error and regular control flow, which can hurt readablity,
119but very little if the handling is simple, for instance providing a default
120value. Similarly, if the error (or alternate outcome) is common enough
121encoding it in the function signature may be good commuication.
123In short, these errors seem to be more effective when errors are likely and
124immediate. High failure operations, especially ones with failures that can
125be handled locally, might be better off using these instead of exceptions.
127Also the implicate handlers and return unions could use exceptions as well.
128For instance, a useful default might handler might be to throw an exception,
129seaching up the stack for a solution if one is not locally provided.
131Or here is a possible helper for unpacking a Result value:
132                forall(otype T, otype E | exception(E))
133                T get_or_throw (Result(T, E) * this) {
134                        if (is_success(this)) {
135                                return get_success(this);
136                        } else {
137                                throw get_failure(this);
138                        }
139                }
140So they can feed off of each other.
Note: See TracBrowser for help on using the repository browser.