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

What happens when no handler is found?



I might as well take this opportunity to mention that Moon has been
studying the proposal that I presented at the meeting and is in the
process of negotiating a variation of that with me. Richard Mlynarik
(MLY@MIT-MC) has also passed along some comments which are being
considered in that revision. We'll be sending out a revised proposal and
accompanying sample implementation as soon as we're satisfied internally
here. It shouldn't be too much longer. Sorry for the delay; this is a
high priority item for us (as I'm sure it is for others on this list).

The rest of this message is a reply to marick@gswd-vms about 
SIGNAL/ERROR[-CASE]...

    Date: Sat, 4 Jan 86 18:31:03 CST
    From: marick@gswd-vms (Brian Marick)
    Message-Id: <8601050031.AA00437@gswd-vms.ARPA>
    To: cl-error-handling@su-ai.arpa
    Subject: What happens when no handler is found?
    Comment: Remailed at SU-AI after delay caused by distribution list error.

    A comment on Kent Pitman's error-handling proposal:   (Reposting)

    Having SIGNAL return NIL if no handler is found makes me nervous;
    it seems to assume that people will explicitly plan for the case where 
    no handler is found, whereas I expect many people will create bugs 
    because they implicitly assume that their condition will be handled.  
    And even if detecting the case of a missing handler by checking whether 
    SIGNAL returns is OK, that doesn't work with SIGNAL-CASE, which is
    expected to return and may return any value.

I think you're just confusing an "error" with a "condition". Not all
conditions are errors. You can think of a condition as being anything
exceptional. For example, if you're working on a clerical task and you
note something "unusual", you might remark it aloud to your boss and
s/he might or might not care. If s/he did not, you wouldn't dwell on it,
you'd just go on. An error, on the other hand, is something that stops
your work and that you can't proceed on without external intervention.
There are plenty more things that you might "remark about" than there
are that you might "stall over". For example, if you worked at Walt
Disney World and the 1,000,000th person walked through the turnstyles,
you might want to make a fuss over him but it wouldn't be an error not
to. This kind of planned ignorability of nevertheless interesting
information is not in error and is still quite useful. It is
underexploited today only because the right tools for playing with it
have not existed until recently.

    There's a distinction between code that's sending a signal to ask 
    for help and code that's sending a signal because there's nothing
    left for it to do, no matter what.  How that distinction meshes with 
    the definitions of SIGNAL, SIGNAL-CASE, ERROR, and ERROR-CASE is 
    not clear enough.

SIGNAL and SIGNAL-CASE do not ask for help. They allow an opportunity
for it.  In a way, ERROR and ERROR-CASE do ask for help. More properly,
though, they simply announce that without help there is nothing more
that will get done in the current computation.

    How if their definitions are modified/clarified to say

    SIGNAL -- control never resumes in the signalling function.  It is an 
    error (with type NO-SUCH-HANDLER or whatever) for no handler to be found.

Signal is not only for errors. It is for any unusual event. Not all
unusual events need to be handled. If you don't subscribe to that idea,
then you should never use SIGNAL and always use ERROR and you'll be all
set.

Examples of things that are not errors but which might want to be
signalled are

 * QUERY -- You might want a query routine which always did a 
   (non-fatal) signal saying that a query was about to happen. One 
   way to proceed from such a query (after inspecting any relevant 
   data) might be by saying what the query should be without entering a
   mode requiring actual I/O. This could be useful in various batch 
   applications. It is not necessarily an error to not handle such a 
   condition. You'd just fall through to other code which would interact 
   at the terminal.

 * DEGENERATE CASES -- Consider a function PPRINT* which took a list of 
   things and pretty printed them to a given stream. Interactively at 
   the console, in home-grown environment-saving programs, or whatever, 
   it might be fine for (PPRINT* L) to quietly print nothing when L was 
   bound to (). Yet for someapplications, it might be useful to notice 
   that nothing had been printed. One way to do this would be for PPRINT*
   to signal a non-fatal exception when it saw an empty list and to assume
   that if no one handled that exception and took control of things that 
   it was OK to proceed.

 * HOOKS -- For example, the Mode hooks which are so popular in MIT's 
   Emacs are an example of something that could be implemented as a 
   non-fatal condition. The program says "I'm selecting Text Mode now" 
   and the handler says "Wait, I'm glad you mentioned that because I 
   have code to run." Had there been no handler, it is still reasonable
   to proceed and select Text Mode.

 * WARNINGS -- The break-on-warnings feature is something which is an 
   example of something which could neatly be handled by non-fatal 
   conditions. Normally, if WARN is called, you can set a variable saying 
   that a breakpoint should be created. However, another way this (or similar
   mechanisms) could be implemented would be for WARN to signal a non-fatal
   condition and to allow people arbitrary control at the time of the WARN. 
   This would allow hooks so that one didn't have to have variables like
   *abort-on-warnings*, *beep-on-warnings*, etc. to allow every possible 
   kind of customization. Instead, one would just write:
    (condition-bind ((compiler:warning 
		       #'(lambda (cond) (ignore cond) (abort))))
      ...)
   or whatever was needed to handle a specific situation in a very general 
   way. If no such condition-bind had been done, the default would be to
   simply continue with the normal I/O done by the WARN routine.

It's fairly easy to conjure examples of places where things like this
can be used.  People who have never used the Lisp Machine condition
system probably don't have the experience to realize the importance of
this sort of thing. Most Lisp Machine users don't even realize the
flexibility at their disposal in this regard, though that is changing. I
and a few other people I know have used this feature to great advantage.

    SIGNAL-CASE -- by default, it is an error (as above) for no handler 
    to be found. If one of the clauses of the SIGNAL-CASE is headed by 
    :NO-HANDLER (or a list containing :NO-HANDLER), that the body of 
    that clause is invoked.

This is an interesting idea but I'm a little worried because it blurs
two notions that I am not sure should be blurred. The first is "how to
proceed a certain way" which is what the kind of clauses I'm proposing
do. The second, which you're proposing, is essentially a shorthand for
condition-bind, which doesn't describe how to proceed but rather
describes how to select how to proceed. For example, under your proposal
you could logically imagine writing a :NO-HANDLER clause which wanted to
proceed using one of the proceed cases in another of the clauses. I'm
not sure if it's wise to invite that sort of confusion.

    ERROR -- control never resumes in the signalling function.  If no 
    handler is found, the signalled condition is passed to the debugger.
    (The distinction between this and SIGNAL is that the error reported 
    to the user is what was discovered by the program, not by the 
    error system.)

I'm not sure what this distinction means. There are no "error systems"
that are not programs. Also, the issue of "reporting" is a kind of
"handling" and is completely logically distinct from the issue of
signalling.

    ERROR-CASE -- just like now.  A :NO-HANDLER clause makes no sense here.

    Brian Marick