This is a read-only archive of lispforum.com. The forum was locked to new users and posts and is preserved here as static HTML from a database snapshot taken on 2019-09-07.

Stepping through code?

14 posts · 13838 views

I have tried the step function on Steel Bank Common Lisp, and I am not sure if I am using it properly. Their manual didn't really explain about step well to a newbie like me. I am trying to learn more about step using a recursive function (Fibonacci), but it just gives me the result without stepping through anything. Here is what it looks like to me:
* (step (fibo 5))
; Evaluating call:
;   (FIBO 5)
; With arguments:
;   5

1] 
When I type next, it just gives me the answer: 8. I tried Clozure as well, but nothing there either...

Re: Stepping through code?

Works for me:
* (defun test-step () (declare (optimize (debug 3)))(print 'a)(print 'b)(print 'c))

TEST-STEP
* (step (test-step))
; Evaluating call:  
;   (TEST-STEP)     
; With arguments:   
;                   

1] :step
; Evaluating call:
;   (PRINT 'A)
; With arguments:
;   A

1] :next

; Evaluating call:
;   (PRINT 'B)
; With arguments:
;   B
A
1] :next

; Evaluating call:
;   (PRINT 'C)
; With arguments:
;   C
B
1] :next

; (TEST-STEP) => C
C
C
Did you actually enabled stepping? Also note that stepping into the form is done with 'step' command, not 'next'. Macro (step ...) macro can contain multiple forms, and top level 'next' will step over those forms.

Re: Stepping through code?

I think a key thing here is the optimization settings. For certain settings of debug, you will not include the instrumentation necessary for stepping through your code. As it turns out, the SBCL default optimization settings don't allow for stepping (this was a surprise to me, did this change from the way it was about a year ago?). So you need the set debug higher than speed, space, and compilation-speed (http://www.sbcl.org/manual/Debugger-Pol ... cy-Control):
SBCL manual wrote:> (max speed space compilation-speed)
If debug is greater than all of speed, space and compilation-speed the code will be steppable (see Single Stepping).

Re: Stepping through code?

Aha. I missed this bit:
(declare (optimize (debug 3)))
I did not put that in my function. I don't suppose there is a way to avoid putting this in each and every function is there? It seems to single step fine now, however, I think when it evaluates a tail recursive call, it seems to just compress the recursion and it doesn't cycle back to the top. So is this setting the most detailed stepping?

Thanks y'all.

Re: Stepping through code?

speech impediment wrote:Aha. I missed this bit:
It is mentioned in the first paragraph of the manual. Reading documentation is usually more productive than guessing. You can set compiler policy using top level declaim. Note that as the Hyperspec says, it is unspecified if delaimations are file-specific or global, but for debugging you can just do a DECLAIM from the REPL before compilation.
speech impediment wrote:I think when it evaluates a tail recursive call, it seems to just compress the recursion and it doesn't cycle back to the top
In tail recursion there is not top to cycle back to by definition.

Re: Stepping through code?

speech impediment wrote:I don't suppose there is a way to avoid putting this in each and every function is there?
You can declaim it before any functions you want to step through.
(declaim (optimize (debug 3)))
speech impediment wrote:I think when it evaluates a tail recursive call, it seems to just compress the recursion and it doesn't cycle back to the top
Not sure what you mean so I will cast a few lines: setting debug setting high often times disables tail call optimization, and at least naive a implementation of Fibonacci doesn't have tail calls anyway, but maybe you are using something more sophisticated? Be that as it may, when I tried this, stepping into the lower fibo calls acted as I expected, cycling back to the top, at least in SBCL. So that it weird... Why don't you post a repl transcript from a fresh image to demonstrate.
Ramarren wrote:In tail recursion there is not top to cycle back to by definition.
Really? It is still calling the function recursively, it just isn't returning to that frame. Am I wrong?
speech impediment wrote:So is this setting the most detailed stepping?
The manual does allude to different levels of stepability, but the debug policy page seems to suggest that (> debug (max speed space compilation-speed)) is the only factor. So unless there are some SBCL internals people here, you could drop by #lisp on irc.freenode.net and ask them there.

Re: Stepping through code?

smithzv wrote:Really? It is still calling the function recursively, it just isn't returning to that frame. Am I wrong?
I might not get what is the 'top' here. I interpreted is a something to do with a higher stack frame, which contains nothing. Obviously execution jumps to the top of the function with new arguments, but that does happen with SBCL stepper.
* (defun recur (list) (declare (optimize (debug 3))) (print (car list))(if list (recur (cdr list))))                                                                                      
STYLE-WARNING: redefining RECUR in DEFUN                                                     

RECUR
* (step (recur '(1 2 3)))
; Evaluating call:       
;   (RECUR '(1 2 3))     
; With arguments:        
;   (1 2 3)              

1] step
; Evaluating call:
;   (PRINT (CAR LIST))
; With arguments:     
;   1                 

1] next

; Evaluating call:
;   (RECUR (CDR LIST))
; With arguments:     
;   (2 3)             
1                     
1] step
; Evaluating call:
;   (PRINT (CAR LIST))
; With arguments:     
;   2                 

1] next

; Evaluating call:
;   (RECUR (CDR LIST))
; With arguments:     
;   (3)               
2                     
1] step
; Evaluating call:
;   (PRINT (CAR LIST))
; With arguments:
;   3

1] next

; Evaluating call:
;   (RECUR (CDR LIST))
; With arguments:
;   NIL
3
1] step
; Evaluating call:
;   (PRINT (CAR LIST))
; With arguments:
;   NIL

1] next

; (RECUR (CDR LIST)) => NIL
; (RECUR (CDR LIST)) => NIL
; (RECUR (CDR LIST)) => NIL
; (RECUR '(1 2 3)) => NIL
NIL
NIL

Re: Stepping through code?

I am sorry that I appear to not have read the manual, but it was hard to decipher where and when to type the keywords like :step and :next and (declare (optimize (debug 3))). I went through the SBCL manual and the hyperspec for about 30 minutes, but I didn't find anything as clear as what Rammaren simply showed. I might be misusing terminology when I am talking about tail recursion... I just assumed that with multiple recursion like the simple Fibonacci function, it is still considered a tail recursive function. When I say cycle back to the top, I just meant to say to start the next cycle of recursion. There's probably a technically correct way to say this... :P
You can declaim it before any functions you want to step through.
I am not sure what you mean, but after perusing the hyperspec, I presume you are talking about having (declare (optimize (debug 3))) outside of the function and replace declare with declaim. Like this:
(declaim (optimize (debug 3)))

(defun test-step () (print 'a)(print 'b)(print 'c))
I tested it with another function without having to insert a declare expression again, so it seems to work...

Once again, I am grateful to you folks for being patient and so helpful.
(defun fibo (n) (declare (optimize (debug 3)))
	(cond ((equal n 0) 1)
			((equal n 1) 1)
			(t (+ (fibo (- n 1)) (fibo (- n 2))))))
* (step (fibo 5))
; Evaluating call:
;   (FIBO 5)
; With arguments:
;   5

1] :step
; Evaluating call:
;   (EQUAL N 0)
; With arguments:
;   5
;   0

1] :next
; Evaluating call:
;   (EQUAL N 1)
; With arguments:
;   5
;   1

1] :next
; Evaluating call:
;   (- N 1)
; With unknown arguments

0] :next
; Evaluating call:
;   (FIBO (- N 1))
; With arguments:
;   4

1] :next
; Evaluating call:
;   (- N 2)
; With unknown arguments

0] :next
; Evaluating call:
;   (FIBO (- N 2))
; With arguments:
;   3

1] :next
; Evaluating call:
;   (+ (FIBO (- N 1)) (FIBO (- N 2)))
; With unknown arguments

0] :next
; (FIBO 5) => 8
8
* 
So if you scroll all the way down, once it creates the two branches, the result is given and there are no more branches to step through. It doesn't even step through the process of addition.

Last edited by speech impediment on , edited 1 time in total.

Re: Stepping through code?

speech impediment wrote:So if you scroll all the way down, once it creates the two branches, the result is given and there are no more branches to step through.
I explained it in very first reply: :next evaluates the expression as the whole and goes to the next one, :step steps into it. You have to use :step to step into recursive calls. Or any calls.
speech impediment wrote:It doesn't even step through the process of addition.
Addition is a primitive operation and doesn't have anything to step through. Well, I suppose there is type dispatch and so, but why would you want to step SBCL internals?

The arguments are separated and stepped through before, it can be seen in the stepper output you posted.

Re: Stepping through code?

I explained it in very first reply: :next evaluates the expression as the whole and goes to the next one, :step steps into it. You have to use :step to step into recursive calls. Or any calls.
This is clear now. :oops:
Addition is a primitive operation and doesn't have anything to step through. Well, I suppose there is type dispatch and so, but why would you want to step SBCL internals?
Well, I was learning about order of evaluation; Normal order vs. Applicative order. How side effects effects does matter in order of evaluation, etc. So I was thinking if I miscalculate order of evaluation and get an outcome I did not understand, it would be helpful. Also, there might be situations where I was dumb enough to mix up something like this: (/ (+ 1 1)(/ 3 3)) and expect that I was doing this: 1/2. However, I do see you point now that I was able to step through my code. Forgive my ignorance again, but I can't seem to find type dispatch in the hyperspec. What is that?

Re: Stepping through code?

speech impediment wrote:Well, I was learning about order of evaluation; Normal order vs. Applicative order.
Common Lisp has strict, left to right order of evaluation of arguments, that is, applicative order. All major Lisps have strict evalution, although I believe Scheme does not specify the order in which arguments are processed.
speech impediment wrote:Forgive my ignorance again, but I can't seem to find type dispatch in the hyperspec. What is that?
I meant dynamic dispatch on type of arguments. Since addition function, like all arithmetic functions, in Common Lisp works on many numeric types (fixnums, bignums, floats, ratios, complex numbers and so on) the system must determine which primitive addition operation to execute promoting the arguments as necessary. Which means that unless full types can be determined at compile time addition is not a primitive operation. But this is internal to the implementation and something one should worry about unless doing microoptimization, which done prematurely is, of course root of all evil.

Re: Stepping through code?

Ramarren wrote: Common Lisp has strict, left to right order of evaluation of arguments, that is, applicative order. All major Lisps have strict evalution, although I believe Scheme does not specify the order in which arguments are processed.
Hmm... that's odd. I was reading about evaluation order in Structure and Interpretation of Computer Programs and it says:
In section 1.1, where we began our discussion of models of evaluation, we noted that Scheme is an
applicative-order language, namely, that all the arguments to Scheme procedures are evaluated when the
procedure is applied.

(define (try a b)
(if (= a 0) 1 b))

Evaluating (try 0 (/ 1 0)) generates an error in Scheme.
From reading this, it implies that Scheme also have strict evaluation... (At least for the implementation used in the book.)

Re: Stepping through code?

speech impediment wrote:From reading this, it implies that Scheme also have strict evaluation... (At least for the implementation used in the book.)
It is strict, but it is not applicative, that is, the order is unspecified. See this note. Maybe SICP is using different definition of applicative. It doesn't really matter all that much, side effects buried in function arguments are rarely a good idea, and depending on order of evaluation of those is even worse.

Re: Stepping through code?

Well, dadgum, you're right. I'm not even going try to find out what definition of "applicative" the book is using. Thanks again Rammaren.