Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

common lisp exception handling (condition and restart)

I have been read the common lisp "Practical Common Lisp" exception handling chapter for days, but I am now so confused with the samples and the explanations, in the meanwhile I tried to write some testing sample, but it didn't work as I expected, below is my testing samples.

  1. Condition definition

    (define-condition evenp-error (error) 
      ((text :initarg :text :reader text)))
    
  2. Define function that prints odd number

    (defun filter-evenp (lst)
      (dolist (x lst)
        (if (not (evenp x)) 
          (print x)
          (error 'evenp-error :text x))))
    
  3. Restart function

    (defun skip-evenp (c) (invoke-start 'skip-evenp))
    
  4. Restart case

    (restart-case (filter-evenp (list 1 2 3 4 5))
      (skip-evenp () nil))
    

All I want to do is to print all odd numbers and skip the even errors, what is wrong with my sample? anybody help? many thanks in advance!!

like image 388
user2015063 Avatar asked Mar 23 '16 12:03

user2015063


People also ask

Does lisp have exception handling?

In Common LISP terminology, exceptions are called conditions. In fact, conditions are more general than exceptions in traditional programming languages, because a condition represents any occurrence, error, or not, which might affect various levels of function call stack.

What is condition system?

The condition system allows the program to deal with exceptional situations, or situations which are outside the normal operation of the program as defined by the programmer. A common example of an exceptional situation is an error, however the Common Lisp condition system encompasses much more than error handling.


2 Answers

Practical Common Lisp is quite detailed but it is true that the condition system might require some time to get used to. You might be interested by Kent Pitman's articles: Exceptional Situations in Lisp and Condition Handling in the Lisp Language Family.

Those are referenced in What's a condition system and why do you want one?. There are also many other references lilke this Wikibooks entry or C2 wiki's CommonLispConditionSystem entry.

Defining a restart

A RESTART-CASE basically says:

I am going to execute this form and I don't care if it signals a condition or not. But if it does and you want to recover from that situation, here are different ways I can work around the problem (retry, ignore, etc.).

You generally cannot say how to recover from an error in the code being called at the call point. In other words, it is filter-evenp which should wrap the code with a restart-case in-order to provide alternative paths. For your example, it would be sufficient to use CERROR, which signals an error while establishing a CONTINUE restart.

(if (evenp x)
  (cerror "Ignore even number" 'evenp-error :text x) 
  (print x))

As an exercise you can try to replace (cerror ...) with an explicit restart-case construct.

Then, if you test your code, you should see your debugger pop up and show you the CONTINUE restart. If you defined your own restart, you can name it differently.

Invoking the restart

In your skip-evenp function, you were invoking a restart that was not established a at this point, and I think you were confused with skip-evenp naming both a restart and a function.

What you should do is handle the error by invoking the restart.

Here, you want the code that signals an error to continue, so you really don't want to unwind the execution stack. That's why you have to use HANDLER-BIND.

(handler-bind ((evenp-error (lambda (e) (invoke-restart 'continue))))
  (filter-evenp '(1 2 3 4)))
1
3    

You can of course extract the anonymous lambda into a custom function, like you did.

like image 172
coredump Avatar answered Sep 30 '22 07:09

coredump


You need to put your RESTART-CASE to where you want to restart the execution at:

(defun filter-evenp (lst)
  (dolist (x lst)
    (restart-case
        (if (not (evenp x))
            (print x)
            (error 'evenp-error :text x))
      (skip-evenp () nil))))

Then you should use HANDLER-BIND to handle the error:

(handler-bind ((evenp-error #'skip-evenp))
  (filter-evenp (list 1 2 3 4 5)))
like image 35
jkiiski Avatar answered Sep 30 '22 08:09

jkiiski