handling errors 
Author Message
 handling errors

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

--



Fri, 08 Aug 2003 08:09:16 GMT  
 handling errors


Quote:
> 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))

;;; 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))))

Quote:
> (test 5)

Argument to apply/funcall is not a function: NIL.
8

Quote:
> (test2 5)

9

Quote:
> (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



Fri, 08 Aug 2003 11:08:23 GMT  
 handling errors


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

Quote:
> (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



Fri, 08 Aug 2003 11:41:56 GMT  
 handling errors


Quote:

> 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))

(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



Fri, 08 Aug 2003 12:52:30 GMT  
 handling errors

Quote:

> 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.

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

> (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

       (undefined-function (condition)
         (values nil condition))))

--
Quid enim est stultius quam incerta pro certis habere, falsa pro veris?
                                                                 -- Cicero
(setq reply-to



Fri, 08 Aug 2003 13:42:46 GMT  
 handling errors

Quote:

> 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))

(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.

--

 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



Fri, 08 Aug 2003 21:08:44 GMT  
 handling errors

Quote:

> ..... 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))

> (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.

--



Sat, 09 Aug 2003 00:05:09 GMT  
 handling errors


Quote:

> > (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.

Quote:
> > (defmacro trap-undefined-functions (&rest forms)

                                         ^^^^
I copied this from the hyperspec, I'm assuming it be better style to use
body.

Quote:
> >   `(catch 'undefined-function
> >      (handler-bind
> >          ((error #'trap-handler))

> > (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.



|
| 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.

Quote:
>   (defmacro ignore-undefined-functions (&body body)
>     `(handler-case

>        (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))

;;; 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



Fri, 08 Aug 2003 23:31:25 GMT  
 handling errors

Quote:
> 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.

Quote:
> 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.

--

 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



Sat, 09 Aug 2003 00:53:36 GMT  
 handling errors

Quote:
Pierre R Mai 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.

--



Sat, 09 Aug 2003 02:24:17 GMT  
 handling errors

Quote:
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))


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.

--



Sat, 09 Aug 2003 02:31:13 GMT  
 handling errors


Quote:
> 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))

> 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



Sat, 09 Aug 2003 04:27:00 GMT  
 handling errors

Quote:

> 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 {*filter*}oline functions on the autoloaded functions that'll pull
in the correct definitions once they are called.

Quote:
> 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.

--

 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



Sat, 09 Aug 2003 08:00:21 GMT  
 handling errors

Quote:

>> > (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.

Quote:
>> 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.



> |
> | 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.

Quote:
> 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.

Quote:
>     (undefined-function () (throw 'undefined-function nil))))
> (defmacro trap-undefined-function (&body forms)
>   `(catch 'undefined-function
>      (handler-bind
>        ((error #'trap-undefined-handler))


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.

Quote:
> ;;; 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



Sat, 09 Aug 2003 08:31:22 GMT  
 handling errors


Quote:

> 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.

Quote:

>   (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



Sun, 10 Aug 2003 00:08:54 GMT  
 
 [ 18 post ]  Go to page: [1] [2]

 Relevant Pages 

1. How to handle error: No records available !

2. Handling Errors form APPEND FROM

3. INVALID HANDLE error when compiling?

4. Handling Errors from APPEND FROM

5. Handling errors (Haskell)

6. handling errors

7. handling errors without exiting

8. GNAT 3.05 distribution, Float Exception Handling Error

9. Handling errors in PyXML/SAX

10. handling errors in input data

11. Handling errors in open

12. HOw do I handle errors?

 

 
Powered by phpBB® Forum Software