From: Johann Hibschman
Subject: handling errors
Date: 
Message-ID: <mtn1bjr0sz.fsf@astron.berkeley.edu>
Hi folks,

I've been reading through the hyperspec, trying to understand errors,
and I've been failing miserably.

What do I do, if I want to trap the undefined-function error, and
continue with a default function, automatically?

I'm more interested in this from the point of view of executing
user-defined code, trapping any undefined functions, then, if it's a
known function, trying to load an associated file to get its
definition.  A bit like the emacs autoload, only different.

To be more concrete, what do I do if I want to do:

(trapping-errors
  (sqrrrt 10)
  (if-undefined-function (use-instead #'sqrt)))

which is, of course, nonsense.

Thanks,

--Johann

-- 
Johann Hibschman                           ······@physics.berkeley.edu

From: Geoffrey Summerhayes
Subject: Re: handling errors
Date: 
Message-ID: <H60k6.254337$f36.10056732@news20.bellglobal.com>
"Johann Hibschman" <······@physics.berkeley.edu> wrote in message
···················@astron.berkeley.edu...
> Hi folks,
>
> I've been reading through the hyperspec, trying to understand errors,
> and I've been failing miserably.
>
> What do I do, if I want to trap the undefined-function error, and
> continue with a default function, automatically?
>
> I'm more interested in this from the point of view of executing
> user-defined code, trapping any undefined functions, then, if it's a
> known function, trying to load an associated file to get its
> definition.  A bit like the emacs autoload, only different.
>
> To be more concrete, what do I do if I want to do:
>
> (trapping-errors
>   (sqrrrt 10)
>   (if-undefined-function (use-instead #'sqrt)))
>
> which is, of course, nonsense.
>
> Thanks,
>
> --Johann

Well, I finally get a chance to start playing with Lisp. Yeah!!
I'm sure there's a better way to do this, so I'm posting what I came up
with, please correct it. I pulled the trap stuff from the HS, hope I got
that part right.

;;; from HS handler-bind slightly modified
;;; replaced trap-errors and error
;;; with undefined-function

(defun trap-handler(condition)
  (format *error-output* "~&~A~&" condition)
  (throw 'undefined-function nil))

(defmacro trap-function-errors(&rest forms)
  `(catch 'undefined-function
     (handler-bind ((undefined-function #'trap-handler))
       ,@forms)))

;;; Pathological function-always fails

(defun dweeb(a)
  (funcall nil a))

;;; First test- see what happens on failure

(defun test (a)
  ;; This I don't quite get, it only seems to work
  ;; with this specific order of unwind-protect and catch
  ;; I'd assumed it would be the other way around from
  ;; the stuff I'd read
  (unwind-protect (catch 'undefined-function
                    (trap-function-errors (return-from test (dweeb a)))
                    (+ 3 a))))

;;; Second test-make sure it works on success

(defun test2 (a)
  (unwind-protect (catch 'undefined-function
                    (trap-function-errors (return-from test2 (+ 4 a)))
                    (+ 3 a))))

> (test 5)
Argument to apply/funcall is not a function: NIL.
8

> (test2 5)
9

> (test2 'a)
In + of (4 A) arguments should be of type NUMBER.

Error: In + of (3 A) arguments should be of type NUMBER.
   1 (continue) Return a value to use.
   2 Supply a new second argument.
<snip>

Geoff
From: Geoffrey Summerhayes
Subject: Re: handling errors
Date: 
Message-ID: <8C0k6.254607$f36.10063216@news20.bellglobal.com>
"Geoffrey Summerhayes" <·············@hNoOtSmPAaMil.com> wrote in message
······························@news20.bellglobal.com...

<SNIP> to the dirty bit... :-(

> (defun test (a)
>   (unwind-protect (catch 'undefined-function
>                     (trap-function-errors (return-from test (dweeb a)))
>                     (+ 3 a))))
>

(defun test (a)
  (trap-fun (return-from test (dweeb a)))
  (+ 3 a)))

Monkeying around a bit more...I couldn't understand why 2 catches, then I
found this worked as well. Getting carried away before I get a good feel for
the language, I'm afraid.

Geoff
From: Geoffrey Summerhayes
Subject: Re: handling errors
Date: 
Message-ID: <iE1k6.254963$f36.10074030@news20.bellglobal.com>
"Geoffrey Summerhayes" <·············@hNoOtSmPAaMil.com> wrote in message
······························@news20.bellglobal.com...
>
> Getting carried away before I get a good feel for
> the language, I'm afraid.

Third times the charm, finally got my head around the hyperspec, I hope...
This version sends the (test 'a) error to top-level from foo, assuming it's
defined, as opposed to passing it on to the (+ 3 a) as it did previously.
Sorry for all the posts in this thread, I'm just a tad over-enthusiastic
about finally getting down to learning Lisp. I'll try to hold off and stop
to look what I've written over, before posting code that almost works, sort
of, if you stretch the truth a bit.

(defun trap-handler (condition)
  (handler-case (signal condition)
    (undefined-function() (throw 'undefined-function nil)))
  (signal condition)) ;; Is this correct? It works with LW

(defmacro trap-undefined-functions (&rest forms)
  `(catch 'undefined-function
     (handler-bind
         ((error #'trap-handler))
       ,@forms)))

(defun test (a)
  (trap-undefined-functions (return-from test (foo a)))
  (+ 3 a))

(test 5)

(defun foo (a) (* 10 a))

(test 5)

(test 'a)

Geoff
From: Paul Foley
Subject: Re: handling errors
Date: 
Message-ID: <m2zofjqld5.fsf@mycroft.actrix.gen.nz>
On Mon, 19 Feb 2001 04:52:30 GMT, Geoffrey Summerhayes wrote:

> Third times the charm, finally got my head around the hyperspec, I hope...
> This version sends the (test 'a) error to top-level from foo, assuming it's
> defined, as opposed to passing it on to the (+ 3 a) as it did previously.
> Sorry for all the posts in this thread, I'm just a tad over-enthusiastic
> about finally getting down to learning Lisp. I'll try to hold off and stop
> to look what I've written over, before posting code that almost works, sort
> of, if you stretch the truth a bit.

> (defun trap-handler (condition)
>   (handler-case (signal condition)
>     (undefined-function() (throw 'undefined-function nil)))
>   (signal condition)) ;; Is this correct? It works with LW

The form inside the HANDLER-CASE already (re)signals the condition; if
it's not UNDEFINED-FUNCTION and the first SIGNAL returned, you'll be
doing it twice.

> (defmacro trap-undefined-functions (&rest forms)
>   `(catch 'undefined-function
>      (handler-bind
>          ((error #'trap-handler))
>        ,@forms)))

> (defun test (a)
>   (trap-undefined-functions (return-from test (foo a)))
>   (+ 3 a))

I'm not really sure what you're trying to achieve, but if you just
want something like IGNORE-ERRORS that only ignores UNDEFINED-FUNCTION
errors,

  (defmacro ignore-undefined-functions (&body body)
    `(handler-case
         (progn ,@body)
       (undefined-function (condition)
         (values nil condition))))

-- 
Quid enim est stultius quam incerta pro certis habere, falsa pro veris?
                                                                 -- Cicero
(setq reply-to
  (concatenate 'string "Paul Foley " "<mycroft" '(··@) "actrix.gen.nz>"))
From: Geoff Summerhayes
Subject: Re: handling errors
Date: 
Message-ID: <t92epaeugsusfd@corp.supernews.com>
"Paul Foley" <·······@actrix.gen.nz> wrote in message
···················@mycroft.actrix.gen.nz...
> On Mon, 19 Feb 2001 04:52:30 GMT, Geoffrey Summerhayes wrote:
>
> > (defun trap-handler (condition)
> >   (handler-case (signal condition)
> >     (undefined-function() (throw 'undefined-function nil)))
> >   (signal condition)) ;; Is this correct? It works with LW
>
> The form inside the HANDLER-CASE already (re)signals the condition; if
> it's not UNDEFINED-FUNCTION and the first SIGNAL returned, you'll be
> doing it twice.

You're right, that's what I had expected, but I misread the results of my
test cases and thought the error wasn't propagating correctly.

> > (defmacro trap-undefined-functions (&rest forms)
                                         ^^^^
I copied this from the hyperspec, I'm assuming it be better style to use
body.


> >   `(catch 'undefined-function
> >      (handler-bind
> >          ((error #'trap-handler))
> >        ,@forms)))
>
> > (defun test (a)
> >   (trap-undefined-functions (return-from test (foo a)))
> >   (+ 3 a))
>
> I'm not really sure what you're trying to achieve, but if you just
> want something like IGNORE-ERRORS that only ignores UNDEFINED-FUNCTION
> errors,

I should have included the OP's message.

"Johann Hibschman" <······@physics.berkeley.edu
<·············@physics.berkeley.edu>> wrote in message
<···················@astron.berkeley.edu>...
|
| Hi folks,
|
| I've been reading through the hyperspec, trying to understand errors,
| and I've been failing miserably.
|
| What do I do, if I want to trap the undefined-function error, and
| continue with a default function, automatically?
|
| I'm more interested in this from the point of view of executing
| user-defined code, trapping any undefined functions, then, if it's a
| known function, trying to load an associated file to get its
| definition. A bit like the emacs autoload, only different.
|
| To be more concrete, what do I do if I want to do:
|
| (trapping-errors
|   (sqrrrt 10)
|   (if-undefined-function (use-instead #'sqrt)))
|
| which is, of course, nonsense.


>   (defmacro ignore-undefined-functions (&body body)
>     `(handler-case
>          (progn ,@body)
>        (undefined-function (condition)
>          (values nil condition))))
>

What I finally ended up with is this:

(defun trap-undefined-handler (condition)
  (handler-case (signal-condition)
    (undefined-function () (throw 'undefined-function nil))))

(defmacro trap-undefined-function (&body forms)
  `(catch 'undefined-function
     (handler-bind
       ((error #'trap-undefined-handler))
       ,@forms)))

;;; Not the best name, implies multiple forms
;;; although I suppose it could be extended
;;; I see this being used as per the original
;;; post as:
;;;
;;; (defined-or (sqrrrt 10) (sqrt 10))
;;;
(defmacro defined-or (true-body false-body)
  (let (g (gensym))
    `(block ,g (trap-undefined-function (return-from ,g ,true-body))
       ,false-body)))

What do you think? Is this a correct use of block? This begs the question,
if one knows that much about the functions involved, why not use fboundp?

Geoff
From: Johann Hibschman
Subject: Re: handling errors
Date: 
Message-ID: <mty9v2iky6.fsf@astron.berkeley.edu>
Geoff Summerhayes writes:
> What I finally ended up with is this:

> (defun trap-undefined-handler (condition)
>   (handler-case (signal-condition)
>     (undefined-function () (throw 'undefined-function nil))))

> (defmacro trap-undefined-function (&body forms)
>   `(catch 'undefined-function
>      (handler-bind
>        ((error #'trap-undefined-handler))
>        ,@forms)))

Okay.  So you're using throw in the handler, because otherwise when
the handler returns, the error will be re-signalled, right?  The
throw gets you out, non-locally.

Somehow this seems very convoluted.


-- 
Johann Hibschman                           ······@physics.berkeley.edu
From: Geoff Summerhayes
Subject: Re: handling errors
Date: 
Message-ID: <t9303pm3t9fr18@corp.supernews.com>
"Johann Hibschman" <······@physics.berkeley.edu> wrote in message
···················@astron.berkeley.edu...
> Geoff Summerhayes writes:
> > What I finally ended up with is this:
>
> > (defun trap-undefined-handler (condition)
> >   (handler-case (signal-condition)
> >     (undefined-function () (throw 'undefined-function nil))))
>
> > (defmacro trap-undefined-function (&body forms)
> >   `(catch 'undefined-function
> >      (handler-bind
> >        ((error #'trap-undefined-handler))
> >        ,@forms)))
>
> Okay.  So you're using throw in the handler, because otherwise when
> the handler returns, the error will be re-signalled, right?  The
> throw gets you out, non-locally.
>
> Somehow this seems very convoluted.

It is. My preference in other languages I've used is to try to handle the
error close to what caused it rather than rely on a generic 'fix' function
that doesn't understand exactly why I was expecting the error at that point.
Of course this is only my second attempt at writing anything in Lisp aside
from the standard little fib and expotential functions everyone has toyed
with at some point in time. I have built up a healthy distrust about using
global variables in code over the years, I feel somewhat safer with the
throw, although I find myself wondering if the thing being thrown might be
better as a gensym also. The point of this exercise, for me, is to have
specific code to run in case of an expected, but not normal, error
condition. Not necessarily an undefined-function, in other words. I'm sure
there is probably a better way to handle it, I've hardly scratched the
surface of the hyperspec.

Geoff
From: Paul Foley
Subject: Re: handling errors
Date: 
Message-ID: <m2r90uqjol.fsf@mycroft.actrix.gen.nz>
On Mon, 19 Feb 2001 10:31:25 -0500, Geoff Summerhayes wrote:

>> > (defmacro trap-undefined-functions (&rest forms)
>                                          ^^^^
> I copied this from the hyperspec, I'm assuming it be better style to use
> body.

Yes.  They should get indented differently.

>> I'm not really sure what you're trying to achieve, but if you just
>> want something like IGNORE-ERRORS that only ignores UNDEFINED-FUNCTION
>> errors,

> I should have included the OP's message.

> "Johann Hibschman" <······@physics.berkeley.edu
> <·············@physics.berkeley.edu>> wrote in message
> <···················@astron.berkeley.edu>...
> |
> | To be more concrete, what do I do if I want to do:
> |
> | (trapping-errors
> |   (sqrrrt 10)
> |   (if-undefined-function (use-instead #'sqrt)))
> |
> | which is, of course, nonsense.

I think Johann was looking for something like

  (handler-bind ((undefined-function (lambda (x) (use-value #'sqrt))))
    (sqrrrt 10))

I.e., Johann wants to say "if you hit an undefined function, use this
one instead and carry on as if nothing unusual had happened"; what
you're saying is "if you hit an undefined function, stop what you're
doing and do this other thing instead", which is rather different.

> What I finally ended up with is this:

> (defun trap-undefined-handler (condition)
>   (handler-case (signal-condition)

I assume you meant (SIGNAL CONDITION), not (SIGNAL-CONDITION); else
you may as well leave out the HANDLER-CASE and just do the THROW.

>     (undefined-function () (throw 'undefined-function nil))))

> (defmacro trap-undefined-function (&body forms)
>   `(catch 'undefined-function
>      (handler-bind
>        ((error #'trap-undefined-handler))
>        ,@forms)))

Why are you catching ERROR only to immediately re-signal it so you can 
catch UNDEFINED-FUNCTION?  Just catch UNDEFINED-FUNCTION in the first
place!  And you'd be better to use HANDLER-CASE, not HANDLER-BIND.

> ;;; Not the best name, implies multiple forms
> ;;; although I suppose it could be extended
> ;;; I see this being used as per the original
> ;;; post as:
> ;;;
> ;;; (defined-or (sqrrrt 10) (sqrt 10))
> ;;;
> (defmacro defined-or (true-body false-body)
>   (let (g (gensym))
>     `(block ,g (trap-undefined-function (return-from ,g ,true-body))
>        ,false-body)))

Dump all that horribly convoluted TRAP-* stuff, and just write

  (defmacro defined-or (true-body false-body)
    (handler-case
        ,true-body
      (undefined-function ()
        ,false-body)))


IF you really feel the need to write such a thing.

-- 
Quid enim est stultius quam incerta pro certis habere, falsa pro veris?
                                                                 -- Cicero
(setq reply-to
  (concatenate 'string "Paul Foley " "<mycroft" '(··@) "actrix.gen.nz>"))
From: Geoff Summerhayes
Subject: Re: handling errors
Date: 
Message-ID: <t955buqp1t9af4@corp.supernews.com>
"Paul Foley" <·······@actrix.gen.nz> wrote in message
···················@mycroft.actrix.gen.nz...
>
> I think Johann was looking for something like
>
>   (handler-bind ((undefined-function (lambda (x) (use-value #'sqrt))))
>     (sqrrrt 10))
>
> I.e., Johann wants to say "if you hit an undefined function, use this
> one instead and carry on as if nothing unusual had happened"; what
> you're saying is "if you hit an undefined function, stop what you're
> doing and do this other thing instead", which is rather different.
>
> > What I finally ended up with is this:
>
> > (defun trap-undefined-handler (condition)
> >   (handler-case (signal-condition)
>
> I assume you meant (SIGNAL CONDITION), not (SIGNAL-CONDITION); else
> you may as well leave out the HANDLER-CASE and just do the THROW.

Yes, that is what I meant.

>
>   (defmacro defined-or (true-body false-body)
>     (handler-case
>         ,true-body
>       (undefined-function ()
>         ,false-body)))
>

I'll be damned, never occurred to me to even try that. I got so wrapped up
in the handler-bind, I missed the forest for the trees. But then, if a
little knowledge is a dangerous thing, when it comes to Lisp I must be
thermonuclear warhead. :-)
Fair warning, gird thy loins, I'll be back with even dumber stuff, I'm sure.

Geoff
From: Pierre R. Mai
Subject: Re: handling errors
Date: 
Message-ID: <87itm6zuoz.fsf@orion.bln.pmsf.de>
Johann Hibschman <······@physics.berkeley.edu> writes:

> What do I do, if I want to trap the undefined-function error, and
> continue with a default function, automatically?

You can easily handle (aka trap) the undefined-function error, using
handler-bind.  The problem is that you can't portably restart the
computation after an undefined-function error:  The standard doesn't
require implementations to establish a restart, nor does it specifiy
what kind of restart should be established.

So any code you write for this kind of functionality will depend on
your implementation.  If your implementation establishes use-value
and/or store-value restarts on encountering undefined functions, you
could use something like this:

(defvar *dynamic-functions* `((bar . ,#'(lambda (x y) (+ x y 42)))
			      (foo . ,#'(lambda (x y) (* x y 42)))))

(defun handle-undefined-function (condition)
  (let* ((name (cell-error-name condition))
	 (entry (assoc name *dynamic-functions*)))
    (if entry
	(use-value (cdr entry))
	(signal condition))))

(defmacro with-dynamic-functions (&body body)
  `(handler-bind ((undefined-function #'handle-undefined-function))
     ,@body))

(with-dynamic-functions (+ (foo 2 4) (bar 2 4)))

=> 384

Instead of use-value, you might want to use store-value if your
implementation provides it, to permanently define the function, or you
might want to use other mechanisms (like loading some file) to define
the function permanently, so that use-value should suffice.

But note that some implementations don't provide any restarts on
undefined-function errors, so that this approach will not work with
them.  In that case you'll have to define dummy functions that invoke
the autoloader.

Regs, Pierre.

-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein
From: Johann Hibschman
Subject: Re: handling errors
Date: 
Message-ID: <mt3ddajzu6.fsf@astron.berkeley.edu>
Pierre R Mai writes:

> Johann Hibschman <······@physics.berkeley.edu> writes:
>> What do I do, if I want to trap the undefined-function error, and
>> continue with a default function, automatically?

> You can easily handle (aka trap) the undefined-function error, using
> handler-bind.  The problem is that you can't portably restart the
> computation after an undefined-function error:  The standard doesn't
> require implementations to establish a restart, nor does it specifiy
> what kind of restart should be established.

Ah, thank you!  That explains it.  Your code is essentially what I was
trying, but I couldn't tell whether I was doing something wrong, CMUCL
was doing something wrong, or CLISP was doing sometihng wrong.  I
guess it was me, as usual.

Is there a list of conditions that use-value is required to be able to
return from?  I looked through the hyperspec, but I couldn't find one.

-- 
Johann Hibschman                           ······@physics.berkeley.edu
From: Pierre R. Mai
Subject: Re: handling errors
Date: 
Message-ID: <87y9v2w7e2.fsf@orion.bln.pmsf.de>
Johann Hibschman <······@physics.berkeley.edu> writes:

> Ah, thank you!  That explains it.  Your code is essentially what I was
> trying, but I couldn't tell whether I was doing something wrong, CMUCL
> was doing something wrong, or CLISP was doing sometihng wrong.  I
> guess it was me, as usual.

The problem you have likely run into is that both CMU CL and CLISP
don't provide use-value restarts for undefined-function errors.  For
those implementations you'll need to use some other approach, like
defining trampoline functions on the autoloaded functions that'll pull
in the correct definitions once they are called.

> Is there a list of conditions that use-value is required to be able to
> return from?  I looked through the hyperspec, but I couldn't find one.

Hmmm, I seem to remember that for some situations a restart is
specified, but now I don't seem to be able to locate any such
situation.  Maybe I'm misremembering...

Regs, Pierre.

-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein
From: Johann Hibschman
Subject: Re: handling errors
Date: 
Message-ID: <mtd7cdxgu0.fsf@astron.berkeley.edu>
Pierre R Mai writes:

> The problem you have likely run into is that both CMU CL and CLISP
> don't provide use-value restarts for undefined-function errors.  For
> those implementations you'll need to use some other approach, like
> defining trampoline functions on the autoloaded functions that'll pull
> in the correct definitions once they are called.

Ah, all right.  Since my mental model is a large application loading
user-defined customization code, perhaps the best way to do it would
be to simply read in the code, then walk through it looking for
errors.

In any case, I just started thinking about this because some Perl
advocate in a far-distant thread was trying to find other languages
which can do some of the odd things Perl can.  It looks like CL can't
portably do this one directly, but it can with a code pre-processing
step, which is easy to write.

Thanks, all.

--J

-- 
Johann Hibschman                           ······@physics.berkeley.edu
From: Christophe Rhodes
Subject: Re: handling errors
Date: 
Message-ID: <sq1ystjblm.fsf@lambda.jesus.cam.ac.uk>
Johann Hibschman <······@physics.berkeley.edu> writes:

> It looks like CL can't
> portably do this one directly, but it can with a code pre-processing
> step, which is easy to write.

Not portably, no. But I also went along this line of thinking once,
and I came up with the following (CMUCL-specific) broken code.

WARNING WARNING WARNING
some combination of compiling these forms and executing them can cause
your computer to crash. Yes, even if it is unix. This code modifies
CMUCL's notion of how to get at a function, which can confuse it
utterly. And it's not going to work for compiled code, either.
WARNING WARNING WARNING

(defun read-new-value ()
  (format t "Enter a value: ")
  (force-output)
  (multiple-value-list (eval (read))))

(defun myctf (name)
  (restart-case
   (let ((lisp::fdefn (lisp::fdefinition-object name nil)))
     (or (and lisp::fdefn (lisp::fdefn-function lisp::fdefn))
         (error 'undefined-function :name name)))
   (use-value (x)
     :report "Use a value"
     :interactive read-new-value
     (progn
       (princ name)
       x))
   (store-value (x)
     :report "Store a value"
     :interactive read-new-value
     (setf (symbol-function name) x))
     ))

(setf (symbol-function 'kernel:%coerce-to-function) #'myctf)

(handler-bind
 ((undefined-function
   #'(lambda (c)
       (let ((symbol (cell-error-name c)))
         (invoke-restart 'store-value
                         #'(lambda (&rest args)
                             (format nil
                                     "<~a>~{~a~}</~a>"
                                     (string symbol)
                                     args
                                     (string symbol))))))))
 (princ (html (head (title "foo"))
       (body (p "This is a paragraph")
             (p "So is this")
             (apply #'ul (mapcar #'li '("This" "is" "a" "list")))))))
             


-- 
Jesus College, Cambridge, CB5 8BL                           +44 1223 524 842
(FORMAT T "(·@{~w ········@{~w~^ ~})" 'FORMAT T "(·@{~w ········@{~w~^ ~})")
From: Johann Hibschman
Subject: Re: handling errors
Date: 
Message-ID: <mtwval82az.fsf@astron.berkeley.edu>
Christophe Rhodes writes:

> WARNING WARNING WARNING
> some combination of compiling these forms and executing them can cause
> your computer to crash. Yes, even if it is unix. This code modifies
> CMUCL's notion of how to get at a function, which can confuse it
> utterly. And it's not going to work for compiled code, either.
> WARNING WARNING WARNING

Oh. My. God.  That's the most awful hack I've seen for a while,
probably not since Andrew Appel tried to get the CS 207 class to cast
an array of machine code to a function in C.  It's beautiful.  I'll
never, ever, use it.  Congratulations.

Cheers,

--J

-- 
Johann Hibschman                           ······@physics.berkeley.edu
From: Vladimir V. Zolotych
Subject: Re: handling errors
Date: 
Message-ID: <3A914435.30174C0F@eurocom.od.ua>
"Pierre R. Mai" wrote:
> 
> ..... you
> could use something like this:
> 
> (defvar *dynamic-functions* `((bar . ,#'(lambda (x y) (+ x y 42)))
>                               (foo . ,#'(lambda (x y) (* x y 42)))))
> 
> (defun handle-undefined-function (condition)
>   (let* ((name (cell-error-name condition))
>          (entry (assoc name *dynamic-functions*)))
>     (if entry
>         (use-value (cdr entry))
>         (signal condition))))
> 
> (defmacro with-dynamic-functions (&body body)
>   `(handler-bind ((undefined-function #'handle-undefined-function))
>      ,@body))
> 
> (with-dynamic-functions (+ (foo 2 4) (bar 2 4)))

I've tried your code. Probably CMUCL (18c) doesn't provide such
restarts. I got the error:

* (with-dynamic-functions (+ (foo 2 4) (bar 2 4)))

Warning: These functions are undefined:
  BAR FOO

Error in KERNEL:%COERCE-TO-FUNCTION:  the function FOO is undefined.

Restarts:
  0: [ABORT] Return to Top-Level.

Debug  (type H for help)

(KERNEL:%COERCE-TO-FUNCTION FOO)
0]

I don't need this code works for me. I'm just 
using every chance to learn more about CL on live examples.

The behavior of restarts seems rather complicated to me.
I even thought it couldn't be explained except as
showing examples. If you have other example(s) such as
the above it will be very appreciated.

-- 
Vladimir Zolotych                         ······@eurocom.od.ua
From: Pierre R. Mai
Subject: Re: handling errors
Date: 
Message-ID: <873ddazka7.fsf@orion.bln.pmsf.de>
"Vladimir V. Zolotych" <······@eurocom.od.ua> writes:

> I've tried your code. Probably CMUCL (18c) doesn't provide such
> restarts. I got the error:

Indeed it doesn't.  The last time this topic came up, there was some
implementation discussion on the cmucl-imp mailing lists, but to this
date noone seems to have invested the time to produce something
workable (some backend work is needed at least on x86, in order to
have this work correctly for compiled code).

Both ACL and LispWorks do provide use-value and store-value restarts,
IIRC.

> The behavior of restarts seems rather complicated to me.
> I even thought it couldn't be explained except as
> showing examples. If you have other example(s) such as
> the above it will be very appreciated.

Which part of restart behaviour seems complicated?  In effect restarts
are ways for your code to communicate to calling code several error
mitigation strategies, that an error-handler might want to invoke in
order to continue processing.

Below is a simple example from MaiSQL.  It implements the connect
function, which creates a new connection to a database.  It receives a
keyword argument that indicates how to handle a new connection to a
database for which a connection already exists.  If the caller invokes
connect with :error as the value of exists, we need to signal an error
if this situation arises.  In order to provide error-handlers, and
especially the user with ways to proceed from this condition, we
supply two restarts called create-new and use-old, which do the obious
thing.  The code is a bit convoluted, due to the interaction between
if-exists and restarts, but the restart related code (in effect the
body of the :error clause to case) is still fairly trivial:

(defun connect (connection-spec
                &key (if-exists *connect-if-exists*)
                (database-type *default-database-type*))
  "Connects to a database of the given database-type, using the type-specific
connection-spec.  if-exists is currently ignored."
  (let* ((db-name (database-name-from-spec connection-spec database-type))
         (old-db (find-database db-name nil))
         (result nil))
    (if old-db
        (case if-exists
          (:new
           (setq result
                 (database-connect connection-spec database-type)))
          (:warn-new
           (setq result
                 (database-connect connection-spec database-type))
           (warn 'maisql-exists-warning :old-db old-db :new-db result))
          (:error
           (restart-case
               (error 'maisql-exists-error :old-db old-db)
             (create-new ()
               :report "Create a new connection."
               (setq result
                     (database-connect connection-spec database-type)))
             (use-old ()
               :report "Use the existing connection."
               (setq result old-db))))
          (:warn-old
           (setq result old-db)
           (warn 'maisql-exists-warning :old-db old-db :new-db old-db))
          (:old
           (setq result old-db)))
        (setq result
              (database-connect connection-spec database-type)))
    (when result
      (pushnew result *connected-databases*)
      (setq *default-database* result)
      result)))

Regs, Pierre.

-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein