I am trying to understand Racket's macros. For that, I built this function for a specific purpose:
(define (while-less-fun max)
(cond [(< a (- max 1))
(and (begin (set! a (+ a 1)) (print "x") a) (while-less-fun max))]
[(< a max)
(begin (set! a (+ a 1)) (print "x") a)]
[(>= a max)
(begin (set! a (+ a 1)) (print "x") a)]
))
It works as intended on the REPL:
> a
2
> (while-less-fun 7)
"x""x""x""x""x"7
> a
7
I tried to transform the function above in the macro bellow. My goal would be to generate the same results:
(define-syntax while-less
(syntax-rules (do)
[(while-less maximo do begin-exp)
(cond [(< a (- maximo 1))
(and begin-exp (while-less maximo do begin-exp))]
[(< a maximo)
begin-exp]
[(>= a maximo)
begin-exp])]))
The expected result was:
(define a 2)
(while-less 7 do (begin (set! a (+ a 1)) (print "x") a))
(while-less 7 do (begin (set! a (+ a 1)) (print "x") a))
Evaluating the second line would print "x" 5 times and change a
to be 7. Also, evaluating the third line would print "x" 1 time and would change a
to be 8.
Unfortunately, this generates an infinite recursion which really surprises me.
What did I do wrong? Why did this infinite recursion happen?
I know that you should avoid writing macros when a function is able to solve the problem. But, is my approach the wrong way of trying to accomplish this task?
In the first cond clause, you call "(while-less maximo do begin-exp)" with the same arguments, the entire body of the macro will be expanded again by calling the same "(while-less maximo do begin-exp)", causing the loop.
What you can do is create a generic while for a condition:
(define-syntax while
(syntax-rules (do)
[(while condition do begin-exp)
(let loop ()
(when condition
begin-exp
(loop)))]))
> (define a 2)
> (while (< a 7) do (begin (set! a (+ a 1)) (print "x") a))
"x""x""x""x""x"
> (while (< a 7) do (begin (set! a (+ a 1)) (print "x") a))
>