In a similar vein, this can also help with debugging by redefining a function to, for example, print its arguments before calling the actual function. Here is a contrived example of a broken factorial function:
(defun fact (x)
(let ((f 1))
(dotimes (i x)
(setq f (* i f)))
f))
fact(3)
should return 6, but returns 0:
22101> (fact 3)
0
Here is one way to redefine *
to print its arguments and then call the original *
:
(let*
(($* *)
(* (lambda (x y)
(format t "(* ~a ~a)~%" x y)
($* x y))))
(fact 3))
Running this gives:
22101> (let* (($* *) (* (lambda (x y) (format t "(* ~a ~a)~%" x y) ($* x y)))) (fact 3))
(* 0 1)
(* 1 0)
(* 2 0)
0
The first multiplication is multiplying by 0, not 1, causing the running product to become 0. (I found it quite satisfying to fix the bug and run the test again.)
Using let*
makes the function binding temporary and ensures the debugging code doesn’t get mixed in with the real code.
While not necessary, note that the format
call above is generating valid Lisp - this makes it easy to copy/paste/run a particular call afterwards to see what it does. This can be useful for graphics programs that don’t generate the right output.
Another possibility is to have the lambda
form compute the result of the function call and put that in the format
call:
(let*
(($* *)
(* (lambda (x y)
(let ((v ($* x y)))
(format t "(* ~a ~a) -> ~a~%" x y v)
v))))
(fact 3))
Note that the locally bound names (here: $*
, x
, y
, v
) should not be in the global namespace or chaos is likely to ensue (I’ve been caught out by this in a related situation).
It occurs to me while writing this that it may be possible to automate the generation of such instrumentation in uLisp itself for user-defined functions by walking the lambda
argument list. Perhaps scanning the documentation string would work for builtin functions; I’m not sure about macros and special forms.