With common lisp and I am assuming the introspection properties. How can I add code to common lisp code that will tell me when a function is called and when has finished executing. I want to take any lisp code and this particular modification to the code. I figure with lisp's AST analysis, this should be possible.
You can use (trace function) for a simple mechanism. For something more involved, here is a good discussion from comp.lang.lisp.
[CL_USER]>
(defun fac (n)
"Naïve factorial implementation"
(if (< 1 n)
(* n (fac (- n 1)))
1))
FAC
[CL_USER]> (trace fac)
;; Tracing function FAC.
(FAC)
[CL_USER]> (fac 5)
1. Trace: (FAC '5)
2. Trace: (FAC '4)
3. Trace: (FAC '3)
4. Trace: (FAC '2)
5. Trace: (FAC '1)
5. Trace: FAC ==> 1
4. Trace: FAC ==> 2
3. Trace: FAC ==> 6
2. Trace: FAC ==> 24
1. Trace: FAC ==> 120
120
[CL_USER]>
If CLOS is an option, it has before, after, and around methods that run before, after and around other methods.
Common lisp has a TRACE function that reports the function, arguments and resulting value of each call specified. Here is the doc page for Steel Bank's version, but you should find something similar in most implementations:
http://www.sbcl.org/manual/Function-Tracing.html
The system also includes a profiler:
http://www.sbcl.org/manual/Deterministic-Profiler.html
Related
The trace macro is very useful for debugging. But it comes to a halt, when used upon any macro. Like if I try to do the following :
CL-USER> (trace push)
Then, it'll give an error saying:
can't use encapsulation to trace anonymous function #<FUNCTION (MACRO-FUNCTION
PUSH) {100053FB9B}>
[Condition of type SIMPLE-ERROR]
Well, that's obvious because the clhs page of trace, clearly defines it upon functions. So, what is the reason for not having any facility for tracing macros in Common Lisp?
Is there any other (unconventional) way to trace macros in Common Lisp?
The Common Lisp standard only mentions tracing of functions. In compiled implementations, macro expansion usually takes place at compile time and thus tracing of macros is usually not supported.
But some Common Lisp implementations can trace macros via a Lisp interpreter (!):
CLISP can trace macros:
[1]> (defmacro foo (a) a)
FOO
[2]> (trace foo)
;; Tracing macro FOO.
(FOO)
[3]> (loop for i below 4 collect (foo i))
1. Trace: (FOO I)
1. Trace: FOO ==> I
1. Trace: (FOO I)
1. Trace: FOO ==> I
1. Trace: (FOO I)
1. Trace: FOO ==> I
1. Trace: (FOO I)
1. Trace: FOO ==> I
(0 1 2 3)
LispWorks is another implementation which supports tracing of macros.
So, what is the reason for not having any facility for tracing macros in Common Lisp?
As mentioned that's the language standard. Beyond the language standard implementations provide all kinds of language extensions in various ways, including the ability of some Lisp interpreters (!) to trace macros.
If the code is already compiled, tracing won't work anyway. Having a Lisp interpreter helps, but implementations are not required to have an interpreter. Lisp interpreter here means an executing engine which works from Lisp code as data.
Using trace on a macro seems a little odd, but it works in CLISP:
(trace push)
(defparameter *stack* '())
(defun push-xy (x y)
(push x *stack*)
(push y *stack*))
; 1. Trace: (push x *stack*)
; 1. Trace: push ==> (setq *stack* (cons x *stack*))
; 1. Trace: (push y *stack*)
; 1. Trace: push ==> (setq *stack* (cons y *stack*))
; ==> push-xy
The standard does not say when it should expand macros so this might happen when functions and lambdas are defined, compiled and sometimes called. Some implementations run the macro twice so you get double the output.
I never use this. I rather use macroexpand-1:
(macroexpand-1 '(push x *stack)))
; ==> (setq *stack (cons x *stack))
; ==> t
If your form returns a new form that uses macros you might want to try macroexpand instead. Its like calling macroexpand-1 over and over until there are no transformation left.
*macroexpand-hook* is the expected method for tracing macros.
http://www.lispworks.com/documentation/HyperSpec/Body/v_mexp_h.htm#STmacroexpand-hookST
I may be asking for the impossible, but am wondering nonetheless.
Is it possible to obtain an analog of the stack-trace for macros? That is, if one set a break-point inside a certain function, the macro-stack-trace would list all macros (perhaps with their inputs) that were macroexpanded to get to that level in the code.
From what I understand, this is currently impossible, but it may be due to my shallow understanding. Does Allegro or SBCL allow or track this kind of information? It appears that this would be really useful for debugging macros.
Any help or advice is appreciated.
As SBCL is a compiler-only implementation meaning all code is automatically compiled (in contrast to being "interpreted"). Calls to macros are expanded as part of compilation, so the fact that something was a macro call is lost.
(defmacro m (n)
`(/ 10 ,n))
(defun foo (x) (m x))
SBCL:
* (foo 0)
debugger invoked on a DIVISION-BY-ZERO in thread
#<THREAD "main thread" RUNNING {1001E06493}>:
arithmetic error DIVISION-BY-ZERO signalled
Operation was /, operands (10 0).
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [ABORT] Exit debugger, returning to top level.
(SB-KERNEL::INTEGER-/-INTEGER 10 0)
0] backtrace
Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {1001E06493}>
0: (SB-KERNEL::INTEGER-/-INTEGER 10 0)
1: (FOO 0)
2: (SB-INT:SIMPLE-EVAL-IN-LEXENV (FOO 0) #<NULL-LEXENV>)
3: (EVAL (FOO 0))
4: (INTERACTIVE-EVAL (FOO 0) :EVAL NIL)
[...]
Some implementations, e.g. Allegro CL, support both interpreted as well as compiled code, the first being helpful in debugging, the second giving better performance. (I show here the command-line interactions. Allegro also offers a GUI to set breakpoints that I'm not familiar with.)
cl-user(4): (foo 0)
Error: Attempt to divide 10 by zero.
[condition type: division-by-zero]
Restart actions (select using :continue):
0: Return to Top Level (an "abort" restart).
1: Abort entirely from this (lisp) process.
[1] cl-user(5): :zoom
Evaluation stack:
(error division-by-zero :operation ...)
->(/ 10 0)
(foo 0)
(eval (foo 0))
[...]
The zoom command takes many options to be more verbose, this shows the form (block foo (m x)):
[1] cl-user(6): :zoom :all t
Evaluation stack:
... 4 more newer frames ...
((:runsys "lisp_apply"))
[... sys::funcall-tramp ]
(excl::error-from-code 17 nil ...)
(sys::..runtime-operation "integer_divide" :unknown-args)
(excl::/_2op 10 0)
->(/ 10 0)
[... excl::eval-as-progn ]
(block foo (m x))
(foo 0)
(sys::..runtime-operation "comp_to_interp" 0)
[... excl::%eval ]
(eval (foo 0))
When you (compile 'foo) the macro calls will be expanded away (like for SBCL) and not show up in backtraces anymore (but Allegro's source-level debugging could help).
In general when it comes to defining macros, to help debugging try to expand into function calls and not big bodies of code. E.g. instead of:
(defmacro with-foo ((var-x var-y thing) &body body)
`(let ((,var-x (..derive from ,thing ..))
(,var-y (..derive from ,thing ..)))
,#body))
I would write it like:
(defmacro with-foo ((var-x var-y thing) &body body)
`(call-with-foo (lambda (,var-x ,var-y) ,#body) ,thing))
(defun call-with-foo (func thing)
(let ((x (..derive from thing ..)
(y (..derive from thing ..))
(funcall func x y)))
so it ends up in the stack trace and is easy to redefine.
See this great post by Kent Pitman:
Incidentally, too, back to CL, you should know that when I write these
WITH-xxx macros, I almost always accompany them with a CALL-WITH-xxx
so that I can do either kind of call. But I find I almost never use
the CALL-WITH-xxx even when I was the one to provide it as an option.
The main reason I write them is not to use them but to make
redefinition easier, since I can redefine the CALL-WITH-xxx without
redefining the macro, and so I don't have to recompile the callers if
the definition changes.
Yes, AllegroCl supports tracing and in general debugging of macros. Quite an effort for not sure how much benefit, but Franz tends to do good things to make CL more viable. Pro tip: there is a an option to turn off what I think they call source-level debugging of macros, and you will want to do that if your code makes heavy use of macros or compilation times can get crazy. Just turn it back on when you think you need the source debugging.
Consider the following code:
CL-USER> (defmacro sum (a b)
(+ a b))
SUM
CL-USER> (let ((alpha 3) (beta -1))
(sum alpha beta))
; in: LET ((ALPHA 3) (BETA -1))
; (SUM ALPHA BETA)
;
; caught ERROR:
; during macroexpansion of (SUM ALPHA BETA). Use *BREAK-ON-SIGNALS* to intercept.
;
; Argument X is not a NUMBER: ALPHA
; (LET ((ALPHA 3) (BETA -1))
; (SUM ALPHA BETA))
;
; caught STYLE-WARNING:
; The variable ALPHA is defined but never used.
;
; caught STYLE-WARNING:
; The variable BETA is defined but never used.
;
; compilation unit finished
; caught 1 ERROR condition
; caught 2 STYLE-WARNING conditions
; Evaluation aborted on #<SB-INT:COMPILED-PROGRAM-ERROR {10030E4223}>.
There are basically two reasons (that I could think of), which contribute to the failure of this code:
1. The macro sum is first evaluated upon two variables alpha and beta, which are sent as symbols into the macro. So, the code to be evaluated inside the macro is:
(+ 'alpha 'beta)
Which won't work, because we can't add two symbols.
2. The variables alpha and beta are lexically bound, because of which, the code of the macro can't access the symbolic values of alpha and beta.
Thus, redefining sum:
CL-USER> (defmacro sum (a b)
(+ (symbol-value a) (symbol-value b)))
WARNING: redefining COMMON-LISP-USER::SUM in DEFMACRO
SUM
Here, the macro sum will evaluate the value of the symbols provided to it. It can only do it, if it is in the scope of the symbols. So, in order to do that, we can make alpha and beta dynamically bound.
Furthermore, in order to check if the dynamic binding is working, we can make a function dynamic-checker, which is defined below:
CL-USER> (defun dynamic-checker ()
(+ alpha beta))
; in: DEFUN DYNAMIC-CHECKER
; (+ ALPHA BETA)
;
; caught WARNING:
; undefined variable: ALPHA
;
; caught WARNING:
; undefined variable: BETA
;
; compilation unit finished
; Undefined variables:
; ALPHA BETA
; caught 2 WARNING conditions
DYNAMIC-CHECKER
And, finally we can evaluate this code in the REPL:
CL-USER> (let ((alpha 3) (beta -1))
(declare (special alpha))
(declare (special beta))
(print (dynamic-checker))
(sum alpha beta))
Which gives us the error:
; in: LET ((ALPHA 3) (BETA -1))
; (SUM ALPHA BETA)
;
; caught ERROR:
; during macroexpansion of (SUM ALPHA BETA). Use *BREAK-ON-SIGNALS* to intercept.
;
; The variable ALPHA is unbound.
;
; compilation unit finished
; caught 1 ERROR condition
2 ; Evaluation aborted on #<SB-INT:COMPILED-PROGRAM-ERROR {1003F23AD3}>.
CL-USER>
Note the 2 in the end of the error code. This is returned by the function dynamic-checker, which adds alpha and beta, even though they aren't it's parameters, which proves that the variables alpha and beta can be accessed dynamically by the members of let.
Therefore, the macro sum should've worked now, because both the problems that occurred earlier are resolved.
But this clearly isn't the case, and I'm missing something.
Any help appreciated.
Interpreter and Compiler vs. interactivity
Common Lisp allows both interpreters and compilers. That you can interactively use a read-eval-print-loop does not mean that the implementation uses an interpreter. It could just incrementally compile the code and then invoke the compiled code. A Lisp interpreter runs the code from the Lisp representation. SBCL doesn't use an interpreter by default. It uses a compiler.
Using an Interpreter
LispWorks has an interpreter. Let's use it:
CL-USER 8 > (defun test ()
(let ((alpha 3) (beta -1))
(declare (special alpha))
(declare (special beta))
(print (dynamic-checker))
(sum alpha beta)))
TEST
CL-USER 9 > (test)
2
2
Thus the code works, since the Lisp interpreter executes the forms and when it sees a macro, then it expands it on the go. The bindings are available.
Let's use the LispWorks stepper, which uses the interpreter. :s is the step command.
(step (test))
(TEST) -> :s
(LET ((ALPHA 3) (BETA -1))
(DECLARE (SPECIAL ALPHA))
(DECLARE (SPECIAL BETA))
(PRINT (DYNAMIC-CHECKER))
(SUM ALPHA BETA)) -> :s
3 -> :s
3
-1 -> :s
-1
(PRINT (DYNAMIC-CHECKER)) -> :s
(DYNAMIC-CHECKER) -> :s
(+ ALPHA BETA) -> :s
ALPHA -> :s
3
BETA -> :s
-1
2
2
2 ; <- output
2
(SUM ALPHA BETA) <=> 2 ; <- macro expansion to 2
2 -> :s
2 ; 2 evaluates to itself
2
2
2
Compilation fails
But we can't compile your code:
CL-USER 10 > (compile 'test)
Error: The variable ALPHA is unbound.
1 (continue) Try evaluating ALPHA again.
2 Return the value of :ALPHA instead.
3 Specify a value to use this time instead of evaluating ALPHA.
4 Specify a value to set ALPHA to.
5 (abort) Return to level 0.
6 Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
The compiler tries to expand the macro, but it does not run the code. Since the LET form isn't executed (the compiler only compiles it to something else, but does not execute it) there is no binding for alpha.
Style
Generally it is a good idea to avoid writing macros which need runtime state from the enclosing code. Such macros would only be executable by an interpreter.
As per user jkiiski's comment, I get the feeling you need to understand when the two operations (macro operations and program/function application) are taking place in typically compiled code. Additionally, i wouldn't recommend writing the macros you've defined, but will work through them for the purpose of education.
Lets start by explicitly declaring two symbols to be special, and bind them to some values:
CL-USER> (defvar alpha 6)
6
CL-USER> (defvar beta 5)
5
This not only establishes two variables as special/dynamic, but also gives them bindings to the values 6 and 5 respectively.
More importantly, by doing this we establish the variables and bindings before the examples in your macro or your code will be evaluated.
Now we run your final form and we should get the following:
CL-USER> (let ((alpha 3) (beta -1))
(declare (special alpha))
(declare (special beta))
(print (dynamic-checker))
(sum alpha beta))
2
11
The first value of 2 is printed due to the printing of (dynamic-checker). The value of 11 is being returned because the macro sum is resolving to the value 11, which then effectively takes the place of the form (sum alpha beta) and is returned in the actual evaluation of your (let ...) code.
The important thing to realise is that the macro is being evaluated at compile-time: that is to say, before the final form of (let ...) is compiled and run. Because it is evaluated before your (let ...) form, your let form does not establish the bindings of 3 and -1 to the symbols alpha and beta when the macro is resolving. I've been cheeky and made explicit bindings to the values 6 and 5 before this, and that's what the macro is seeing.
After the macro is successfully resolved, your let form effectively becomes:
(let ((alpha 3) (beta -1))
(declare (special alpha))
(declare (special beta))
(print (dynamic-checker))
11)
And that explains why we now get no errors, what caused the errors in your previous example, and why we got the output from my additions specified above, and we can explicitly see when the macro/let form are being resolved.
How can I get the number of arguments supplied to a Lisp function like in bash with the variable $0? (I saw a similar question but it does not give the answer.)
It's not clear exactly what you're asking, but in Common Lisp you can use an &rest argument to collect an indeterminate number of arguments into a list. Using length you can see how many were provided. For instance:
CL-USER> (defun numargs (&rest arguments)
(length arguments))
NUMARGS
CL-USER> (numargs 1 2 3)
3
CL-USER> (numargs 1 2 3 4 5)
5
CL-USER> (numargs)
0
Since the question has the sbcl tag, you might be interested in SBCL-specific solutions. sb-introspect:function-lambda-list looks relevant:
CL-USER> (sb-introspect:function-lambda-list 'cons)
(SB-IMPL::SE1 SB-IMPL::SE2)
CL-USER> (sb-introspect:function-lambda-list 'numargs)
(&REST ARGUMENTS)
If you examine the lambda list, you can determine how many arguments a function can take.
I've written a Lisp function like this:
(defun power (base exponent)
(if (= exponent 0)
1
(* base (power (- exponent 1)))))
When I try to call it, however, I get some errors:
CL-USER 2 > (power 2 3)
Error: POWER got 1 arg, wanted at least 2.
1 (abort) Return to level 0.
2 Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
CL-USER 3 : 1 > (power 2)
Error: POWER got 1 arg, wanted at least 2.
1 (abort) Return to level 1.
2 Return to debug level 1.
3 Return to level 0.
4 Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
CL-USER 4 : 2 > (power 2 3 4)
Error: POWER got 3 args, wanted 2.
1 (continue) Ignore the extra arg.
2 (abort) Return to level 2.
3 Return to debug level 2.
4 Return to level 1.
5 Return to debug level 1.
6 Return to level 0.
7 Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
What's going on here? If I give it two arguments, it thinks I gave it one. If I give it three, it thinks I gave it three. If I give it one, it thinks I gave it one...
It is the recursive call that only has one argument:
(power (- exponent 1))
It should be like this:
(power base (- exponent 1))
The recursive call is your problem. You forgot to pass the base in as the first argument.
(* base (power (- exponent 1)))))
should be:
(* base (power base (- exponent 1)))))
Compile your functions. In LispWorks use c-sh-c to compile a definition in the editor.
Here in the REPL:
CL-USER 18 > (defun power (base exponent)
(if (= exponent 0)
1
(* base (power (- exponent 1)))))
POWER
CL-USER 19 > (compile 'power)
;;;*** Warning in POWER: POWER is called with the
;;; wrong number of arguments: Got 1 wanted 2
The Compiler will already tell you that there is a problem with the code.
Note that the LispWorks Listener (the REPL) does not compile. You have to compile the definitions you enter at the Listener with the function COMPILE. Alternatively you can type the definitions into an editor window and compile it from there (by either compiling the file, the buffer or the expression). LispWorks also has features to locate code where an error happens.
Lisp comes with the function expt, so no need to define your own.
(Unless this is an exercise or homework, in which case you might want to look at more efficient methods, such as exponentiation by squaring.)