[plt-scheme] Best practices when writing macros; avoid name shadowing?

From: Grant Rettke (grettke at acm.org)
Date: Sun Feb 3 23:02:29 EST 2008

Tonight I was working on a problem from
_The_Scheme_Programming_Language_3rd_Edition,
in particular Exercise 3.2.5 in
http://www.scheme.com/tspl3/further.html#./further:h2.

In this problem you goal is to augment a given version of `let` to
handle `named let` using letrec.

I worked on the problem and came up with solution that made sense and
according to my rudimentary
testing worked. The expanded code was what I expected to see, and that
code did what I expected. When I checked
the answer, it was wrong, but only by a bit, and I didn't understand
why since it worked. The error was later demonstrated
to me.

Here is the source:

(custom-let ([fun '(a b c)])
             (custom-let fun ([ls fun])
                        (unless (null? ls)
                          (printf "~s\n" (car ls))
                          (fun (cdr ls)))))

My wrong version of expanded code:

((lambda (fun)
     (letrec ([fun (lambda (ls)
                     (unless (null? ls)
                       (printf "~s\n" (car ls))
                       (fun (cdr ls))))])
       (fun fun))) '(a b c))

The correct way to expand the code:

((lambda (fun)
     ((letrec ((fun (lambda (ls)
                      (unless (null? ls)
                        (printf "~s\n" (car ls))
                        (fun (cdr ls)))))) fun)
      fun)) '(a b c))

I see the error, the function name in the letrec block shadows the
name of the list in the enclosing lambda block.

Is this a lesson that you learn writing macros, to be sure not to
shadow variables in your expansion?

There must be tons of hairy situations that folks who heavily utilize
the macro system have to avoid!

All thoughts appreciated on this one.


Posted on the users mailing list.