gracelang / language

Design of the Grace language and its libraries
GNU General Public License v2.0
6 stars 1 forks source link

Specification of Exception Kinds is missing #113

Closed apblack closed 7 years ago

apblack commented 8 years ago

The Grace spec used to describe the top levels of the Exception hierarchy. It no longer does. I don't know when it was removed, or by whom, but I remember writing it, and wonder why it's not there.

Arguably the lower levels of the hierarchy should be in the standard library document, not in th language spec, but it seems to me that the upper levels need to be in the spec. Certainly, the NoSuchMethod exception is part of the language itself, as are other programming errors, such as returning from a block that is no longer in the context of the method that created it.

Here is what I found had been deleted:

Kinds of Exception

Grace defines a hierarchy of kinds of exception. All exceptions have the same type, that is, they understand the same set of requests. However, there are various kinds of exception, corresponding to various kinds of exceptional situation.

The exception hierarchy classifies these kinds of exception using ExceptionKind objects, which have the following type:

type ExceptionKind = Pattern & {
    parent -> ExceptionKind
    // answers the exceptionKind that is the parent of this exception in the
    // hierarchy. The parent of exception is defined to be exception. The parent
    // of any other exceptionKind is the exception that was refined to create it.

    refine (name:String) -> ExceptionKind
    // answers a new exceptionKind, which is a refinement of self.

    raise (message:String)
    // creates an exception of this kind, terminating the current execution,
    // and transferring control to an appropriate handler.

    raise (message:String) with (data:Object)
    // similar to raise(), except that the object data is associated with the
    // new exception.
}

Because ExceptionKinds are also Patterns, they support the pattern protocol (match, &, and |). Perhaps more pertinently, this means that they can be used as the argument of the catch blocks in a try()catch()... construct.

At the top of the hierarchy is the Exception object; all exceptions are refinements of exception. There are three immediate refinements, of Exception:

Notice that there is no category for "expected" exceptions. This is deliberate; expected events should not be represented by exceptions, but by other values and control structures. For example, if you you have a key that may or may not be in a dictionary, you should not request the at method and catch the NoSuchObject exception. Instead, you should request the at(_)ifAbsent(_) method.

Each exception is matched by the ExceptionKind that was raised to create it, and all of the ancestors of that kind of exception. Because Exception is the top of the exception hierarchy, it matches all exceptions.

Exceptions have the following type.

type Exception = type {
        exception -> ExceptionKind    // the exceptionKind of this exception.
        message -> String     // the message that was provided when this exception was raised.

        data -> Object      // the data object that was associated with this exception
        // when it was raised, if there was one. Otherwise, the string "no data".

        lineNumber -> Number        // the source code line number
        // of the raise request that created this exception.

        moduleName -> String        // the name of the module
        // containing the raise request that created this exception.

        backtrace -> List<String>
        // a description of the call stack at the time that this exception was raised.
        // backtrace.first is the initial execution environment; backtrace.last is the
        // context that raised the exception.
}

Exceptions are distinguished by the name passed to the refine method when they were created. Exception packets also contain a "data" field, which may be populated with any object using the raise(_)with(_) method on exceptions:

MyException.raise "A message" with (anObject)

There is no behaviour or requirement attached to this object. It is simply stored to be used by the exception handler if desired.

A single handler may be defined for more than one kind of exception using the | pattern combinator:

try {
    ...
} case { e : MyError | AnotherError ->
    ...
}

This handler will be run when either MyError or AnotherError is raised inside the try block.

kjx commented 8 years ago

Dunno. probably chopped out in error. Feel free to put it back in.

apblack commented 7 years ago

I added it in commit 7c351f.

apblack commented 7 years ago

It would be good if other people proofread this section!