From: Tim Bradshaw
Subject: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <ey3k7hhnhin.fsf@cley.com>
I have a situation where I want to signal a condition which is not an
error, but I want it not to be possible for the application not to
deal with it.  This sounds a bit strange: the particular case is that
I'm getting input from the user, and the user has said `abort' in some
way.  I need to find *something* legal to return (and I need to be
told what that is: I can't just return a semantically legal value but
not say that `this didn't come from the user'), or allow the
application to handle the situation itself.  I don't really want to
treat the situation as an error, but I do want to make sure that the
application does tell me what to do (either by invoking a restart, or
by otherwise handling the condition).  If the application does not
give me some advice on what to do, that is definitely a bug.

(Here `the application' is `whoever calls the code that deals with
getting input').

I suppose one answer is `don't be silly, this is an error, make the
condition a subclass of ERROR and just signal it with ERROR.

Another possible answer is: signal it with ERROR (so it can't be
ignored), but make it not be an error.  This smells pretty bad.

Finally (what I do now) is have a couple of conditions, one of which
is the same as the other but mixes in ERROR, and signal one, then if
that is not handled signal the other (the ERROR one) with ERROR.

And finally finally, maybe I need to rethink the whole protocol
properly.  This is obviously really the right thing to do...

Any opinions on this?

Thanks

--tim

From: Dave Bakhash
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <c294r8ldkkg.fsf@no-knife.mit.edu>
Tim Bradshaw <···@cley.com> writes:

> I have a situation where I want to signal a condition which is not an
> error, but I want it not to be possible for the application not to
> deal with it.

I think this is a fairly common use of the condition system.  The
conditions you define go hand-in-hand with the main loops in your
program, and so how you end up defining and then signaling your
conditions should be a function of how you intend for your program to be
used.

I would consider inheriting from `warning', since you want to let the
condition through, but at the same time, it's the kind of thing that you
want to handle most of the time.  Warnings won't stop your application
from running, but they will trigger a message somewhere that this
happened.  So in this unique case, you may want to signal your
conditions with `warn'.  If they don't get handled, then (and only then)
you get your warning.

Furthermore, while debugging, it may sometimes be useful to set
*break-on-signals* to 'warning (or to whatever specific subclass of
'warning you define).

dave
From: Kaz Kylheku
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <cf333042.0301071140.e8dda75@posting.google.com>
Tim Bradshaw <···@cley.com> wrote in message news:<···············@cley.com>...
[ snip ]
> by otherwise handling the condition).  If the application does not
> give me some advice on what to do, that is definitely a bug.

Okay, so it seems like you want to force the application to give that
advice. You don't want the application to be able to ``snub'' your
code, and just invoke an non-local exit that ignores your restarts.
You want to regain control in such a way that the application
programmer would have to go out of his or her way to wrestle it away
from you. In other words, you want less flexibility than what the
wonderful condition system offers. :)

In that light, have you considered maybe not using the condition
system at all? Perhaps your input module needs a required parameter: a
function object which is called to obtain advice. The user must
implement the function and pass it to you, and you will call that
function when that abort situation arises. The function must return
some indication of what to do (effectively, the advice) such as a
keyword symbol chosen from some set you prescribe.

Ultimately, you can provide a *-CASE macro which allows the user to
code the advice in terms of a programming language construct which
implicitly invokes the input module, and provides required clauses to
handle every condition.
From: Tim Bradshaw
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <ey3n0mcn0wu.fsf@cley.com>
* Kaz Kylheku wrote:

> Okay, so it seems like you want to force the application to give that
> advice. You don't want the application to be able to ``snub'' your
> code, and just invoke an non-local exit that ignores your restarts.
> You want to regain control in such a way that the application
> programmer would have to go out of his or her way to wrestle it away
> from you. In other words, you want less flexibility than what the
> wonderful condition system offers. :)

No, I *do* want it to be able to grab control back (unilaterally), but
I want it to be able to say other things as well, such as `retry' or
`use this default'.  I think that the real truth is that I need to
think more about protocol issues here...

--tim
From: Christophe Rhodes
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <sqznqddmx4.fsf@lambda.jcn.srcf.net>
Tim Bradshaw <···@cley.com> writes:

> by otherwise handling the condition).  If the application does not
> give me some advice on what to do, that is definitely a bug.
> [...]
> Another possible answer is: signal it with ERROR (so it can't be
> ignored), but make it not be an error.  This smells pretty bad.

This doesn't seem so bad to me... STORAGE-CONDITION is already one of
these objects -- the idea being that IGNORE-ERRORS does not ignore
STORAGE-CONDITION, which is a reflection of an imperfect outside
world, not an error in the program.
 
Cheers,

Christophe
-- 
http://www-jcsu.jesus.cam.ac.uk/~csr21/       +44 1223 510 299/+44 7729 383 757
(set-pprint-dispatch 'number (lambda (s o) (declare (special b)) (format s b)))
(defvar b "~&Just another Lisp hacker~%")    (pprint #36rJesusCollegeCambridge)
From: Kent M Pitman
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <sfwr8bpt05i.fsf@shell01.TheWorld.com>
Christophe Rhodes <·····@cam.ac.uk> writes:

> Tim Bradshaw <···@cley.com> writes:
> 
> > by otherwise handling the condition).  If the application does not
> > give me some advice on what to do, that is definitely a bug.
> > [...]
> > Another possible answer is: signal it with ERROR (so it can't be
> > ignored), but make it not be an error.  This smells pretty bad.
> 
> This doesn't seem so bad to me... STORAGE-CONDITION is already one of
> these objects -- the idea being that IGNORE-ERRORS does not ignore
> STORAGE-CONDITION, which is a reflection of an imperfect outside
> world, not an error in the program.

Yes, IGNORE-ERRORS is the thing to reason about.  An asynchronous
keyboard interrupt is not a program error, and so should go unhandled,
not be trapped by IGNORE-ERRORS.  If IGNORE-ERRORS could trap it,
program behavior would be quite unpredictable.
From: Joe Marshall
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <4r8khcgv.fsf@ccs.neu.edu>
Kent M Pitman <······@world.std.com> writes:

> Yes, IGNORE-ERRORS is the thing to reason about.  An asynchronous
> keyboard interrupt is not a program error, and so should go unhandled,
> not be trapped by IGNORE-ERRORS.  If IGNORE-ERRORS could trap it,
> program behavior would be quite unpredictable.

Franz Allegro Common Lisp disagrees with you here.

I had to shadow IGNORE-ERRORS and replace it with something that
explicitly *didn't* catch user keyboard interrupts.
From: Kent M Pitman
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <sfwptr8sdt8.fsf@shell01.TheWorld.com>
Joe Marshall <···@ccs.neu.edu> writes:

> Kent M Pitman <······@world.std.com> writes:
> 
> > Yes, IGNORE-ERRORS is the thing to reason about.  An asynchronous
> > keyboard interrupt is not a program error, and so should go unhandled,
> > not be trapped by IGNORE-ERRORS.  If IGNORE-ERRORS could trap it,
> > program behavior would be quite unpredictable.
> 
> Franz Allegro Common Lisp disagrees with you here.

Well, it's not standardized, so they're permitted to disagree without
being nonconforming...

> I had to shadow IGNORE-ERRORS and replace it with something that
> explicitly *didn't* catch user keyboard interrupts.

Although not nonconforming, it does seem "philosophically wrong" to me
to have a non-deterministic, asynchronous, externally generated situation
be trapped by in this way.

I think abort should be a restart, not a condition.  It should be trapped
by with-simple-restart, not by ignore-errors.  I think that keyboard
interrupts are conditions, but are not errors.

But that's just my opinion.

I'll be interested to hear what someone from franz says on this.
From: Nils Goesche
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <lyel7pngq9.fsf@cartan.de>
Tim Bradshaw <···@cley.com> writes:

> Another possible answer is: signal it with ERROR (so it can't be
> ignored), but make it not be an error.  This smells pretty bad.

I like this one and use it myself now and then :-) Why do think it's
bad?

> Finally (what I do now) is have a couple of conditions, one of which
> is the same as the other but mixes in ERROR, and signal one, then if
> that is not handled signal the other (the ERROR one) with ERROR.

Sounds just like a clumsy emulation of the former variant to me.

Regards,
-- 
Nils G�sche
"Don't ask for whom the <CTRL-G> tolls."

PGP key ID 0x0655CFA0
From: Tim Bradshaw
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <ey3fzs5nfrm.fsf@cley.com>
* Nils Goesche wrote:

> I like this one and use it myself now and then :-) Why do think it's
> bad?

Hmm.  Well, I think that it's slightly dubious to use ERROR on
something that isn't an error.  But maybe not.

--tim
From: Tim Bradshaw
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <ey3adidnfc9.fsf@cley.com>
* I wrote:

> Hmm.  Well, I think that it's slightly dubious to use ERROR on
> something that isn't an error.  But maybe not.

Ah.  Actually one reason is that I have a division of conditions into
user and system conditions - a user condition is something to do with
what the user did, or said (so a syntax error in a config file is a
user condition), while a system condition is an issue in the program
itself (`I can't find the XML parser' is a system condition).  (And of
course, there are un-checked for things which are neither, and get
caught and result in a `oops, please report this right now' type
message.)

The user saying abort is a user condition.  The application failing to
handle that abort is a system condition (because it's a bug).  So they
need to be disjoint.

--tim
From: Nils Goesche
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <ly65t1neah.fsf@cartan.de>
Tim Bradshaw <···@cley.com> writes:

> * I wrote:
> 
> > Hmm.  Well, I think that it's slightly dubious to use ERROR on
> > something that isn't an error.  But maybe not.
> 
> Ah.  Actually one reason is that I have a division of conditions
> into user and system conditions - a user condition is something to
> do with what the user did, or said (so a syntax error in a config
> file is a user condition), while a system condition is an issue in
> the program itself (`I can't find the XML parser' is a system
> condition).  (And of course, there are un-checked for things which
> are neither, and get caught and result in a `oops, please report
> this right now' type message.)
> 
> The user saying abort is a user condition.  The application failing
> to handle that abort is a system condition (because it's a bug).  So
> they need to be disjoint.

Hmmm.  You could set up a *DEBUGGER-HOOK* that signals a system
condition, for instance:

(define-condition system-condition (error)
  ()
  (:report (lambda (cnd stream)
             (declare (ignore cnd))
             (format stream "System."))))

(define-condition user-condition ()
  ()
  (:report (lambda (cnd stream)
             (declare (ignore cnd))
             (format stream "User."))))

(defun test ()
  (let ((*debugger-hook* (lambda (cnd old)
                           (declare (ignore cnd old))
                           (error 'system-condition))))
    (handler-case
        (error 'user-condition)
      (system-condition (cnd) (format t "Ha: ~A" cnd)))))

CL-USER 29 > (test)
Ha: System.
NIL

Not sure if this is something sensible to do...

Regards,
-- 
Nils G�sche
"Don't ask for whom the <CTRL-G> tolls."

PGP key ID 0x0655CFA0
From: Kent M Pitman
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <sfwn0mdszfu.fsf@shell01.TheWorld.com>
Tim Bradshaw <···@cley.com> writes:

> * Nils Goesche wrote:
> 
> > I like this one and use it myself now and then :-) Why do think it's
> > bad?
> 
> Hmm.  Well, I think that it's slightly dubious to use ERROR on
> something that isn't an error.  But maybe not.

The Lisp Machine used to force anything you gave to ERROR to be an
error (dynamically mixing in dbg:debugger-condition, I think) in order
to signal it with error.  It was not possible to signal a non-error
with error.  We relaxed this restriction in CL exactly because the two
issues seemed orthogonal and because it's confusing to have the thing
that gets signaled not be the thing you gave it (or a directly
predictable derivative of it) and because it's sometimes useful to do
precisely the thing you're suggesting.

However, the thing that feels weird, I think, is that you're going
into the debugger without having had an opportunity to realize it. 
If this is a synchronous error, you might feel better if you did:

 (or ;; first pass looking for handlers, even outside an IGNORE-ERRORS
     (signal 'abort-attempted)
     ;; second pass looking for handlers, considering IGNORE-ERRORS
     (error 'abort-not-handled))

Or you might prefer

 (let ((condition (make-condition 'abort-attempted)))
    (or (signal condition)
        (invoke-debugger condition)))

In this case, you effectively do the same as error would do but don't 
have to feel like it was an error.

I'm slightly puzzled as to why this is a condition at all, though.
Why isn't it just a restart and why aren't you just doing:
 (or (invoke-restart 'abort)
     (error 'abort-not-handled))
which is precisely what the abort function does, btw.  The invoke-restart
would do a search up the stack for aborts even outside of ignore-errors
because aborts are not conditions but restarts.
(In Dylan, conditions and restarts are unified, and aborts are non-error
conditions, so in there this would be equivalent of my earlier suggestion.)
From: Tim Bradshaw
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <ey365t0ooev.fsf@cley.com>
* Kent M Pitman wrote:

>  (or ;; first pass looking for handlers, even outside an IGNORE-ERRORS
>      (signal 'abort-attempted)
>      ;; second pass looking for handlers, considering IGNORE-ERRORS
>      (error 'abort-not-handled))

This is what I'm doing now....

> I'm slightly puzzled as to why this is a condition at all, though.
> Why isn't it just a restart and why aren't you just doing:
>  (or (invoke-restart 'abort)
>      (error 'abort-not-handled))

But this is what I should do, I think.  Or maybe ... Well.  One of the
things I want to be able to do is to allow the application, at a
fairly high level, to specify default values for things.  I was
planning to do this by establishing (in the user input code) USE-VALUE
restarts, and then having the application say (somewhere way above all
the individual calls):

(handler-bind
  ((user-aborted-input
    (lambda (c)
      (use-value (value-maybe-computed-from c))))
   ...)
 ...)

I think that if I do things the restart way, then I need to wrap a
restart with an appropriate value around each call to a user-input
function, so each call would need to look like:

(restart-case
  (io-function ...)
  (use-application-value ()
   ...))

and this makes it harder to centralise this information.

As I said, I really need to actually think harder about the
protocol...

--tim
From: Tim Bradshaw
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <ey3r8bon7xu.fsf@cley.com>
* I wrote:
> As I said, I really need to actually think harder about the
> protocol...

Actually, I think I can do it with restarts alone.  If I establish a
restart called (say) DEFAULT-VALUE with RESTART-BIND, which fails to
transfer control but just returns a suitable default value, then I can
do, in the guts of the function:

(let ((d (find-restart 'default-value)))
  (if d 
      (use-value (invoke-restart d ...)))
    (error ...))

I'm not sure if I like this, but it works...

--tim
From: Kent M Pitman
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <sfwwulgwz6a.fsf@shell01.TheWorld.com>
Tim Bradshaw <···@cley.com> writes:

> * I wrote:
> > As I said, I really need to actually think harder about the
> > protocol...
> 
> Actually, I think I can do it with restarts alone.  If I establish a
> restart called (say) DEFAULT-VALUE with RESTART-BIND, which fails to
> transfer control but just returns a suitable default value, then I can
> do, in the guts of the function:
> 
> (let ((d (find-restart 'default-value)))
>   (if d 
>       (use-value (invoke-restart d ...)))
>     (error ...))
> 
> I'm not sure if I like this, but it works...

Ah, here's a case where FIND-RESTART with no condition argument makes
kind of vague sense.  I think I said a week or two ago that I knew no
such case.
From: Kent M Pitman
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <sfwsmw4wz0s.fsf@shell01.TheWorld.com>
Kent M Pitman <······@world.std.com> writes:

> Tim Bradshaw <···@cley.com> writes:
> 
> > * I wrote:
> > > As I said, I really need to actually think harder about the
> > > protocol...
> > 
> > Actually, I think I can do it with restarts alone.  If I establish a
> > restart called (say) DEFAULT-VALUE with RESTART-BIND, which fails to
> > transfer control but just returns a suitable default value, then I can
> > do, in the guts of the function:
> > 
> > (let ((d (find-restart 'default-value)))
> >   (if d 
> >       (use-value (invoke-restart d ...)))
> >     (error ...))
> > 
> > I'm not sure if I like this, but it works...
> 
> Ah, here's a case where FIND-RESTART with no condition argument makes
> kind of vague sense.  I think I said a week or two ago that I knew no
> such case.

Uh, I spoke too soon ... sorta.  Well, it IS a reasonable reason to use
no condition in FIND-RESTART--i.e., when there is no condition ongoing.

But I have to say I find this use of restarts to be kind of icky.  Why don't
you just do:

 (defvar *default-value-finder* nil)
 ...
 (if *default-value-finder*
     (use-value (funcall *default-value-finder*))
   (error ...))

It doesn't really need all the restart overhead, which includes introspective
information that the user generally does not need.
From: Kent M Pitman
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <sfw1y3oydsm.fsf@shell01.TheWorld.com>
Tim Bradshaw <···@cley.com> writes:

> > I'm slightly puzzled as to why this is a condition at all, though.
> > Why isn't it just a restart and why aren't you just doing:
> >  (or (invoke-restart 'abort)
> >      (error 'abort-not-handled))
> 
> But this is what I should do, I think.  Or maybe ... 

Well, the other way you could do it is:

 (condition-bind ((keyboard-interrupt
                    #'(lambda (c)
                        (signal c) ;make this a default handler
                        (invoke-restart 'abort))))
   (or (signal 'keyboard-interrupt :kind 'abort)
       (error 'abort-not-handled)))

or


 (condition-bind ((keyboard-interrupt
                    #'(lambda (c)
                        (when (eq (keyboard-interrupt-kind c) 'abort)
                          (invoke-restart 'abort)))))
   (your-application-here))

and then later

 (or (signal 'keyboard-interrupt :kind 'abort)
     (error 'abort-not-handled))

Well.  One of the

> things I want to be able to do is to allow the application, at a
> fairly high level, to specify default values for things.  I was
> planning to do this by establishing (in the user input code) USE-VALUE
> restarts, and then having the application say (somewhere way above all
> the individual calls):
> 
> (handler-bind
>   ((user-aborted-input
>     (lambda (c)
>       (use-value (value-maybe-computed-from c))))
>    ...)
>  ...)
> 
> I think that if I do things the restart way, then I need to wrap a
> restart with an appropriate value around each call to a user-input
> function, so each call would need to look like:
> 
> (restart-case
>   (io-function ...)
>   (use-application-value ()
>    ...))
> 
> and this makes it harder to centralise this information.

Can you make some of this more concrete?  I'm not following something...

> As I said, I really need to actually think harder about the
> protocol...

This might be the same as my previous statement. ;)
From: Tim Bradshaw
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <ey3isx0n0s5.fsf@cley.com>
* Kent M Pitman wrote:

> Can you make some of this more concrete?  I'm not following
> something...

Not really (you are following it, but I haven't thought it through!)

>> As I said, I really need to actually think harder about the
>> protocol...

> This might be the same as my previous statement. ;)

Yes, I think so.

--tim
From: Kalle Olavi Niemitalo
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <8765t0k9ni.fsf@Astalo.y2000.kon.iki.fi>
Kent M Pitman <······@world.std.com> writes:

>  (condition-bind ((keyboard-interrupt
>                     #'(lambda (c)
>                         (signal c) ;make this a default handler
>                         (invoke-restart 'abort))))
>    (or (signal 'keyboard-interrupt :kind 'abort)
>        (error 'abort-not-handled)))

I can't find CONDITION-BIND in the CLHS; it appears to be a
special operator in Zetalisp.  Can one substitute HANDLER-BIND
directly, or are the semantics slightly different?
From: Barry Margolin
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <lPGS9.22$bW.1774@paloalto-snr1.gtei.net>
In article <··············@Astalo.y2000.kon.iki.fi>,
Kalle Olavi Niemitalo  <···@iki.fi> wrote:
>Kent M Pitman <······@world.std.com> writes:
>
>>  (condition-bind ((keyboard-interrupt
>>                     #'(lambda (c)
>>                         (signal c) ;make this a default handler
>>                         (invoke-restart 'abort))))
>>    (or (signal 'keyboard-interrupt :kind 'abort)
>>        (error 'abort-not-handled)))
>
>I can't find CONDITION-BIND in the CLHS; it appears to be a
>special operator in Zetalisp.  Can one substitute HANDLER-BIND
>directly, or are the semantics slightly different?

They're essentially the same.  Kent's just showing his age.

-- 
Barry Margolin, ······@genuity.net
Genuity, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Kent M Pitman
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <sfwu1gksdzf.fsf@shell01.TheWorld.com>
Kalle Olavi Niemitalo <···@iki.fi> writes:

> Kent M Pitman <······@world.std.com> writes:
> 
> >  (condition-bind ((keyboard-interrupt
> >                     #'(lambda (c)
> >                         (signal c) ;make this a default handler
> >                         (invoke-restart 'abort))))
> >    (or (signal 'keyboard-interrupt :kind 'abort)
> >        (error 'abort-not-handled)))
> 
> I can't find CONDITION-BIND in the CLHS; it appears to be a
> special operator in Zetalisp.  Can one substitute HANDLER-BIND
> directly, or are the semantics slightly different?

Oops. I meant handler-bind.  Heh... we renamed it in CL, but it does 
mostly the same thing in CL.  Sorry for the confusion.
From: Kenny Tilton
Subject: Re: signalling conditions which are not ERRORs with ERROR
Date: 
Message-ID: <3E1B0067.1020901@nyc.rr.com>
Tim Bradshaw wrote:
> ....I do want to make sure that the
> application does tell me what to do (either by invoking a restart, or
> by otherwise handling the condition).  If the application does not
> give me some advice on what to do, that is definitely a bug.

IIUC, this is an unusual attempt by a library to play lifeguard for the 
calling application. If so, then unusual efforts are justified, such as 
error-ing a non-error, or really any of the other things you proposed.

I have not worked it out very far, but I find myself wondering if there 
is some way you can enforce the protocol /on the way in/, say by 
requiring as a parameter a, what?, condition to be signalled 
(errored?)... or maybe a callback abort handler? Jes thinkin out loud...


-- 

  kenny tilton
  clinisys, inc
  http://www.tilton-technology.com/
  ---------------------------------------------------------------
"Cells let us walk, talk, think, make love and realize
  the bath water is cold." -- Lorraine Lee Cudmore