As the compiler optimizes the (tail-call) stack away, getting to the root cause of an error can sometimes be a challenge in Scheme.
It would be great if the more experienced members in the Gambit community can share the techniques they use to debug programs, especially in production.
Thanks,
--Vijay
Afficher les réponses par date
Tail-calls are not an optimization in Scheme, they are required by the semantics of the language. This gives a behavior at run time that is surprizing for programmers used to other languages where all calls are handled the same, i.e. the caller’s activation frame is kept on the stack for the duration of the call to the callee even for tail-calls. So the way tail-calls are handled in Scheme are an “optimization” only when compared to these other languages.
For any compiler, optimizations can get in the way of debugging because some contextual information may be missing if the compiler determines that this information is not needed at run time. In addition to removing the parent activation frame on tail-calls, the Gambit compiler removes from the activation frame any variable that has become “dead” (a variable whose value won’t be used in the rest of the function). This is actually related to the problem of space leakage and the “safe for space” property. To have more information available when debugging the general idea is to disable these optimizations.
Here’s an example of debugging a tail-recursive function f:
% cat tc.scm (define (f n) (if (= n 0) (/ 1 n) (f (- n 1))))
(f 5)
% gsc -debug tc.scm % gsi -:dar tc *** ERROR IN ##load -- Divide by zero (/ 1 0)
,be
0 ##load 1 ##main
As you can see all the activation frames have been removed from the stack.
Adding these declarations to the top of tc.scm will disable the optimizations:
(declare (not proper-tail-calls) ;; don't optimize tail calls (not optimize-dead-local-variables) ;; don't remove dead variables )
Now we get:
% gsc -debug tc.scm % gsi -:dar tc *** ERROR IN f, "tc.scm"@8.7 -- Divide by zero (/ 1 0)
,be
0 f "tc.scm"@8:7 (/ 1 n) n == 0 1 f "tc.scm"@9:7 (f (- n 1)) n == 1 2 f "tc.scm"@9:7 (f (- n 1)) n == 2 3 f "tc.scm"@9:7 (f (- n 1)) n == 3 4 f "tc.scm"@9:7 (f (- n 1)) n == 4 5 f "tc.scm"@9:7 (f (- n 1)) n == 5 6 | tc| "tc.scm"@11:1 (f 5) 7 ##load 8 ##main
Note that the declarations don’t have to be global. They can be put inside the functions where you want to disable the optimizations.
Hope that helps!
Marc
On Oct 7, 2016, at 2:00 AM, Vijay Mathew vijay.the.lisper@gmail.com wrote:
As the compiler optimizes the (tail-call) stack away, getting to the root cause of an error can sometimes be a challenge in Scheme.
It would be great if the more experienced members in the Gambit community can share the techniques they use to debug programs, especially in production.
Thanks,
--Vijay _______________________________________________ Gambit-list mailing list Gambit-list@iro.umontreal.ca https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list
What I try to do is debug the code in the interpreter and set
(generate-proper-tail-calls #f)
Then you'll find, e.g.,
(define (f n)
(if (= n 0) (/ 1 n) (f (- n 1))))
(f 5)
*** ERROR IN f, (console)@11.7 -- Divide by zero (/ 1 0) 1> ,b 0 f (console)@11:7 (/ 1 n) 1 f (console)@12:7 (f (- n 1)) 2 f (console)@12:7 (f (- n 1)) 3 f (console)@12:7 (f (- n 1)) 4 f (console)@12:7 (f (- n 1)) 5 f (console)@12:7 (f (- n 1)) 6 (interaction) (console)@13:1 (f 5) 1>
Brad
On 10/07/2016 09:18 AM, Bradley Lucier wrote:
What I try to do is debug the code in the interpreter and set
(generate-proper-tail-calls #f)
Sorry, I should have said that you have to do this before loading any code.
Brad
Then you'll find, e.g.,
(define (f n)
(if (= n 0) (/ 1 n) (f (- n 1))))
(f 5)
*** ERROR IN f, (console)@11.7 -- Divide by zero (/ 1 0) 1> ,b 0 f (console)@11:7 (/ 1 n) 1 f (console)@12:7 (f (- n 1)) 2 f (console)@12:7 (f (- n 1)) 3 f (console)@12:7 (f (- n 1)) 4 f (console)@12:7 (f (- n 1)) 5 f (console)@12:7 (f (- n 1)) 6 (interaction) (console)@13:1 (f 5) 1>
Brad
Note that generate-proper-tail-calls only works in the interpreter. Moreover, the documentation says about generate-proper-tail-calls:
[Note: this procedure is DEPRECATED and will be removed in a future version of Gambit. Use the proper-tail-calls declaration instead.]
The declaration works in interpreted and compiled code.
Marc
On Oct 7, 2016, at 9:24 AM, Bradley Lucier lucier@math.purdue.edu wrote:
On 10/07/2016 09:18 AM, Bradley Lucier wrote:
What I try to do is debug the code in the interpreter and set
(generate-proper-tail-calls #f)
Sorry, I should have said that you have to do this before loading any code.
Brad
Then you'll find, e.g.,
(define (f n)
(if (= n 0) (/ 1 n) (f (- n 1))))
(f 5)
*** ERROR IN f, (console)@11.7 -- Divide by zero (/ 1 0) 1> ,b 0 f (console)@11:7 (/ 1 n) 1 f (console)@12:7 (f (- n 1)) 2 f (console)@12:7 (f (- n 1)) 3 f (console)@12:7 (f (- n 1)) 4 f (console)@12:7 (f (- n 1)) 5 f (console)@12:7 (f (- n 1)) 6 (interaction) (console)@13:1 (f 5) 1>
Brad
Gambit-list mailing list Gambit-list@iro.umontreal.ca https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list
On 10/07/2016 09:52 AM, Marc Feeley wrote:
Note that generate-proper-tail-calls only works in the interpreter. Moreover, the documentation says about generate-proper-tail-calls:
[Note: this procedure is DEPRECATED and will be removed in a future version of Gambit. Use the proper-tail-calls declaration instead.]
The declaration works in interpreted and compiled code.
Ah, bad habits die hard ...
Brad