The outer let
is evaluated during the expansion of the macro; it binds the symbol bodysym
. The inner let is evaluated when the resulting code is executed; it binds a symbol whose name is body
.
Here is a simpler, mostly equivalent macro, which simply uses the symbol body
:
(defmacro condition-case-unless-debug (var bodyform &rest handlers)
`(let ((body (lambda () ,bodyform)))
(if debug-on-error
(funcall body)
(condition-case ,var
(funcall body)
,@handlers))))
The problem with this simpler macro is that the code it expands to uses the symbol body
. This interferes with the use of body
as a variable name in the place where it is used. Instead of body
, the macro could use a symbol like condition-case-unless-debug/body
which isn't used anywhere else, but even that would fail if the macro is used multiple times, one nested in the other. Hence the macro uses make-symbol
to create a different symbol each time it is expanded. Creating fresh symbols is a common concern when writing macros that expand to code that uses temporary variables.
The difference between this macro and a plain condition-case
should be easy to see from the simplified version above: the resulting code checks the variable debug-on-error
, and if an error occurs, instead of applying the error handlers, the code lets the debugger catch the error. This can be useful while debugging error handling, but you wouldn't use it in production code.
,@
in front of a variable name is a way to expand a variable under backquotes, like ,
except that it treats the value of the variable as a list and splices it. Thus in (condition-case ,var (funcall body) ,@handlers)
the variable handlers
should contain a list of handlers; this is equivalent to (append (list 'condition-case var '(funcall body) ) handlers)
.