Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • doc/working/exception/design.txt

    r465ed18 r88e0080  
    11Design of Exceptions and EHM in Cforall:
    22
    3 
    4 Exception Instances:
    5 Currently, exceptions are integers (like errno).
    6 
    7 They are planned to be the new "tagged structures", which allows them to
    8 exist in a simple hierarchy which shared functionality throughout. However
    9 the tagged structures are not yet implemented so that will wait.
    10 
    11 A single built in exception is at the top of the hierarchy and all other
    12 exceptions are its children. When you match against an exception, you match
    13 for an exception and its children, so the top of the hierarchy is used as a
    14 catch-all option.
    15 
    16 The shared functionality across exceptions has not been finalized, but will
    17 probably include things like human readable descriptions and default handlers.
     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.
    186
    197
    20 Throwing:
    21 There are currently two kinds of throws, "throw" for termination and
    22 "throwResume" for resumption. Both keywords can be used to create a throw
    23 statement. The kind of throw decides what handlers may catch the exception
    24 and weither control flow can return to the throw site.
     8What is an Exception:
    259
    26 Syntax
    27 "throw" exception ";"
    28 "throwResume" 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.
    2912
    30 Non-local throws are allowed for resumption only. A target is an object with
    31 a stack, with which it may propagate and handle the exception.
     13Option 1: Strutures
    3214
    33 Syntax
    34 "throwResume" exception "_At" target ";"
     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.
    3519
    36 Termination throws unwind the stack until a handler is reached, control moves
    37 onwards from the end of the handler. Resumption throws do not unwind, if a
    38 handler is found and control will return to the throw after the exception is
    39 handled.
     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.
     24
     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.
     29
     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.
     33
     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.
     37
     38Option 2: Tags
     39
     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.
     43
     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.
     48
     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.
     56
     57Option N: ...
     58
     59This list is not complete.
    4060
    4161
    42 Catching:
    43 The catch and handle of an exception is preformed with a try statement, which
    44 also can have finally clauses to exceute on exit from the scope.
     62Seperating Termination and Resumption:
    4563
    46 Syntax
    47 "try"
    48         try-block
    49 ( ("catch" | "catchResume")
    50   "(" exception_type [identifier] [";" conditional_expression] ")"
    51         catch-block
    52 )*
    53 ("finally"
    54         finally-block
    55 )?
     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.
    5668
    57 Either at least 1 handler clause or the finally clasue must be given on each
    58 try block. Each handler clause handles 1 of the two types of throws. Each
    59 handler also specifies a type of exception it handles, and will handle all
    60 children exceptions as well. In addition, a conditional expression which, if
    61 included, must be true for the handler to catch the exception.
    62 
    63 The two types of handlers may be intermixed. Multiple handlers catching the
    64 same type may also be used, to allow for fallbacks on false conditionals.
     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.
    6573
    6674
    67 Implementation Overview:
     75Conditional Matching:
    6876
    69 The implementation has two main parts. The first is just a collection of the
    70 support definitions we need, the data types and functions used within the
    71 exception handling code. Second is a translation from Cforall code to C code
    72 that uses those definitions to throw, catch and handle exceptions.
     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.
    7381
    74 Termination handlers call a specially annotated function, passing it inner
    75 functions that act as the varius sub-blocks. Termination throws use the
    76 unwind library that checks the underlying code for those annotations. Each
    77 time one is found some magic is used to check for a matching handler, if one
    78 is found control goes to the special function which excecutes the handler and
    79 returns.
    80 
    81 Resumption handlers maintain a linked list of stack allocated nodes that have
    82 the handler functions attached. Throwing a resumption exception traverses this
    83 list, and calls each handler, the handlers handle the exception if they can
    84 and return if they did or not.
    85 
    86 Finally clauses just use stack cleanup to force a nested function, which has
    87 the code from the finally clause, to execute when we leave that section.
     82Also it assumes one sweep, that might also be a problem. But might also give
     83it an advantage over re-throws.
    8884
    8985
    90 Alternative Error Handling: Return Unions
     86Alternatives: Implicate Handlers & Return Unions
    9187
    92 Return unions (Maybe and Result), are types that can encode a success or
    93 other result in a single value. Maybe stores a value or nothing, Result stores
    94 a value or an error.
     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.
    9592
    96 For errors that are usually handled quite close to where they occur, these
    97 can replace exceptions.
     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.
    9898
    99 They tend to be faster and require similar or less amounts of code to handle.
    100 However they can slow down the normal path with some extra conditionals and
    101 can mix the normal and exceptional control flow path. If handling the error
    102 is simple, and happens relatively frequently, this might be prefered but in
    103 other cases it just hurts speed and readability.
     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.
     103
     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.
     108
     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.
     112
     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.
     117
     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.
    104122
    105123In short, these errors seem to be more effective when errors are likely and
     
    107125be handled locally, might be better off using these instead of exceptions.
    108126
    109 Also the return unions could use exceptions as well. Getting the improper
    110 side of a return union might throw an exception. Or we can provide helpers
    111 for results withe exceptions as in:
     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.
     130
     131Or here is a possible helper for unpacking a Result value:
    112132                forall(otype T, otype E | exception(E))
    113133                T get_or_throw (Result(T, E) * this) {
    114                         if (has_value(this)) {
    115                                 return get_value(this);
     134                        if (is_success(this)) {
     135                                return get_success(this);
    116136                        } else {
    117                                 throw get_error(this);
     137                                throw get_failure(this);
    118138                        }
    119139                }
     140So they can feed off of each other.
Note: See TracChangeset for help on using the changeset viewer.