My experience is that dynamic scope almost never causes the problems. I think it happened to me something like two times in last four years, and then I fixed it easily. I have two functions in my library,
protect1 and
protect2 I'm using to check whether that is the case. I write something like
Code: Select all
(define (my-function ...) ...) ; using variables x, y, z
(protect1 'my-function '(x y)) ; in case I want overshadowing of z, say, it is some global counter
and that
protect1 will change the names of the variables in something like my-function.x, my-function.y, my-function.z, so accidental overshadowing with variables from other functions is impossible. There is no performance penalty for doing that. For example:
Code: Select all
(set 'set-protected1
(lambda(function/macro-name definition-code variables)
(set function/macro-name
(expand definition-code
(map (lambda(x)
(list x (sym (string function/macro-name "." x))))
variables)))))
(set 'protect1 (lambda(function/macro-name variables)
(set-protected1 function/macro-name
(eval function/macro-name)
variables)))
;-------------
; your code with additional 'protection'
(define-macro (dolist-while)
(letex (var (args 0 0)
lst (args 0 1)
cnd (args 0 2)
body (cons 'begin (1 (args))))
(let (res)
(catch (dolist (var lst)
(if (set 'res cnd) body (throw res)))))))
(protect1 'dolist-while '(var lst cnd body res))
(define (test1 a-list)
(dolist-while (x a-list (!= x 'd)) (println x)))
(define (test2 lst)
(dolist-while (x lst (!= x 'd)) (println x)))
(test1 '(a b c d e f))
(test2 '(a b c d e f))
;----------------
It works.
Here is how your macro looks like after 'protection':
Code: Select all
(lambda-macro ()
(letex (dolist-while.var (args 0 0) dolist-while.lst (args 0 1) dolist-while.cnd
(args 0 2) dolist-while.body
(cons 'begin (1 (args))))
(let (dolist-while.res)
(catch
(dolist (dolist-while.var dolist-while.lst)
(if (set 'dolist-while.res dolist-while.cnd)
dolist-while.body
(throw dolist-while.res)))))))
In most cases, I do not use that
protect1, only if my program has error I cannot find, then I test whether it is caused by name overshadowing. But almost never it is. I use it "just in case" when I write functions for library. Particularly, I used
protect1 to protect
protect1.
This solution is equivalent to officially recommended use of contexts.
Very rarely, accidental overshadowing might happen between two instances of the same function or macro (if function calls itself recursively.) It is particularly severe form of the name overshadowing, it cannot be solved with
protect1 or with contexts, and for that purpose, I have
protect2. It is very sophisticated function, and its use has high price in performances, but the fact is, I never needed it, neither once. It sits in my library 'just in case'. For years now. Discussion on this forum has shown that noone actually had that problem at all.
With very little experience, one learns how to prevent accidental overshadowing, and problems are very rare in practice.