
While macro with continuations
Quote:
> I have been looking at using a while macro recently and most times I
> see people say that a while is equivalent to letrec. From Scheme and
> Art of Programming by Springer and Friedman, they say:
> (while test expr1 expr2 ...)
> is equivalent to:
> (letrec ((loop (lambda() (if test (begin expr1 expr2 ... (loop))))))
> (loop)
> )
> The problem I have with this is that you are extending the environment
> (which you may or may not want to do with while) and more importantly,
> this can cause problems if you happen to use something called loop in
> your original while statement.
There is a way to implement 'while' without introducing any bindings
in the scope of the test expression or the body. No extra bindings at
all -- for gensym-variables or otherwise. It appears that's what the
original author wanted.
The implementation is based on the following transformation:
(while test body)
=====>
(
(lambda (test-thunk body-thunk)
(letrec
((loop (lambda ()
(if (test-thunk) (begin (body-thunk) (loop))))))
(loop)))
(lambda () test) ; test-thunk
(lambda () body) ; body-thunk
)
This implementation can be generalized so that (while test body)
returns the result of the last evaluation of the body, or #f
(
(lambda (test-thunk body-thunk)
(letrec
((loop (lambda (prev-result)
(if (test-thunk) (loop (body-thunk)) prev-result))))
(loop #f)))
(lambda () test) ; test-thunk
(lambda () body) ; body-thunk
)
The following macro implements this construction:
(define-macro (while test body1 . bodyrest)
`(
(lambda (test-thunk body-thunk)
(letrec
((loop (lambda (prev-result)
(if (test-thunk) (loop (body-thunk)) prev-result))))
(loop #f)))
(lambda () ,test) ; test-thunk
))
The macro does not introduce any binding in the scope of its argument
expressions. Therefore, the macro is hygienic by construction.
(let ((i 0) (j 0))
(while (< i 2) (display i) (display ": ")
(display
(while (< j i) (display j) (display " ") (set! j (+ 1 j)) j))
(newline) (set! i (+ 1 i))))
==prints==>
0: #f
1: 0 1
We can even avoid any binding construction whatsoever save lambda:
(while test body)
=====>
(
(
(lambda (f)
(f f)) ; The U combinator
(
(lambda (test-thunk body-thunk) ; The "While combinator"
(lambda (self)
(lambda (prev-value)
(if (test-thunk) ((self self) (body-thunk)) prev-value))))
(lambda () test) ; test-thunk
(lambda () body) ; body-thunk
)
)
#f ; Initial value for prev-value
)
This is an example of programming with combinators. It's well known
that combinatorial logic avoids any (visible) variables. It follows
then that macros in the combinational style are hygienic by
construction. Hmm, another solution to the hygiene problem?
Sent via Deja.com http://www.deja.com/
Before you buy.