This brief discussion of Antidebugging techniques has four parts:
  1. Antibugging Techiques
  2. the function error
  3. the function cerror
  4. the macro assert

Antibugging Techniques

Lot's of Lisp functions work on input of various types. The same function that builds a list of numbers can build a list of lists. There are times though were the type of the arguments to your function matter. For the same reason that the builtin function '+' doesn't like to be passed lists as arguments ( e.g. (+ '( 2 3 ) 3 ) will land you in the debugger. ). Additionally the format or structure of the arguments may be incorrect. Or maybe you might possibly be passing bad data to yourself... You might call antibugging code, defensive driving.

In other languages the compiler does some type checking for you at compile-time. In lisp you have to do checking at runtime (compile time checking can be done also... it mostly turned off by default though). Or you can do no checking at all ( which is what most probably have been doing in this class all along. And that is/was OK ).

There are several ways of signaling errors in Lisp. See CLtL2 chapter 24 for a full discussion of this topic. The following are three of the more commonly used error signalers.

error

The function error has the following specification
	error format-string &rest args
This function is used to signal a fatal error. It is impossible to continue from this kind of error. [ Note: error will never return to its caller. ]

Examples are ( you can invoke these at the Listener ) :

	(error  "Do not pass go, Do not collect $200. Go directly to Jail.")

	(error  "the value of ~A : ~S  is not a legal value" 'x "foo" )

cerror ( continuable error )

Sometimes errors are not fatal and there is some default action that can be taken. This type of error can be signaled with the cerror function. The specification for this function is:
	cerror  continue-format-string error-format-string &rest args
The error-format-string is displayed upon entry into the debugger. It has the same functionality of the format string argument to error.

The continue-format-string is displayed in the Restarts Window in MCL (and as one of the options in the initial debugger message in LCL ). The following are two examples.

	(cerror  "Continue on your merry way" 
		 "Halt we are the Borg, resistance is futile...")

	(defun average ( num-list ) 
		(cond  ( (null num-list ) 
			 (cerror "Use 0 as the average"
			         "Average of the empty list is undefined.")   
			 0 )
			(t (/ (reduce #'+ num-list ) (length num-list)))))
For the latter entering (average '( 1 2 3) is fine. While entering (average nil ) can be continued and return the default number.

But sometimes it would be nice to correct the input to a function on the fly and continue. For that see the next function.

assert

Some languages provide assertion macros that can be used to signal an error if certain preconditions are not met. In languages like 'C' these macros allow you to exit ( or core dump ) and print out a possiblly useful error message.
		Malloc failed due to lack of heap space. Have a nice day. :-)
Of course since there is a debugger built into lisp, Lisp can do something slightly better than dump you back at the prompt.

The assert macro in lisp has the following specification:

	assert test-form  (place...) format-string format-args 
If the test-form fails then the format-string is printed. The place is any legal first argument to a setf form. The remainder is the used to print the error message. After a new value is bound the test-form is redone. If the new value isn't legal either, the user is queried yet again.
	(let  ( (foo nil ))
              (assert  (floatp foo )
			(foo )
			"The variable FOO : ~S is not a float" foo )
	      foo )
Evaluating this will drop you into the debugger. If you try to continue at this point you will be prompted to give another value. Give foo the value 1. This will land you in the debugger yet again. At this point give foo a value such as 1.23 or some other float. The expression will return that value since the last line of the let is simply foo. The following is a slightly more complicated example.
        (let ((foo nil )
	      (bar nil ))
            (assert ( and  foo bar )
	             (foo bar )
	           "Either Foo : ~S or Bar : ~S are nil" foo bar )
	    (list foo bar ))

Back to 2360 Homepage
Last modified: by Lyman S. Taylor(lyman@cc.gatech.edu)
(c) copyright Lyman S. Taylor 1994, All rights reserved