control and macros
After reading the posts on control operators, Vlado Zlatanov decided to
look into prompt, control, fcontrol and the rest of the
goodies in control.ss.
So based on the example from the blog post I did this python-like snippet:
(define/y (step) (yield 1) (yield 2) (yield 3) 'finished)He decided to look into turning it into a macro, such that the above ends up being correct code. When he got stuck, he asked on our mailing list and the resulting dialog was so informative that I decided to blog it. My first replay was this suggestion:
(define-syntax define/y (syntax-rules () [(_ yield-name (name arg ...) body ...) (define (name arg ...) (define exit-with #f) (define (switch-control-context th) (call/cc (lambda (k) (set! exit-with k) (th)))) (define (yield-name x) (call/cc (lambda (resume-here) (set! name (lambda () (switch-control-context (lambda () (resume-here 'dummy))))) (exit-with x)))) (switch-control-context (lambda () body ...)))]))
control.ss to simplify the code. Second, use
syntax-case to eliminate the need for the programmer-user of
define/y to specify the name of yield.
So, here is the prompt-based code:
(require (lib "control.ss")) (define-syntax define/y (syntax-rules () [(_ yield-name (name arg ...) body ...) (define (name arg ...) (define (yield-name x) (control resume-here (set! name (lambda () (prompt (resume-here 'dummy)))) x)) (prompt body ...))])) (define/y yield (step) (yield 1) (yield 2) (yield 3) 'finished) (equal? '(1 2 3) (list (step) (step) (step)))
yield. The definition of define/y shows how to
mark the return point with prompt and how to switch to this
point with control so that your generator can resume the
traversal at the place where it was interrupted.
For the second challenge, I wrote this definition:
(require (lib "control.ss")) (define-syntax (define/y stx) (syntax-case stx () [(_ (name arg ...) body ...) (with-syntax ((yield-name (datum->syntax-object stx 'yield))) (syntax (define (name arg ...) (define (yield-name x) (control resume-here (set! name (lambda () (prompt (resume-here 'dummy)))) x)) (prompt body ...))))])) (define/y (step) (yield 1) (yield 2) (yield 3) 'finished) (equal? '(1 2 3) (list (step) (step) (step)))
syntax-case and
with-syntax to inject yield into the body of
define/y.
In response, Vlado wrote "but isn't this non-hygienic." Here is my
response:
Hygiene is a uniformity default imposed on the expander with a provision for programmers to choose the non-default. I chose this word carefully when I coined the phrase. So what you have *is* a hygienic solution.In other words, injecting an identifier into a macro is not a violation of hygiene at all. It's just means using the full power of the macro system.

0 comments:
Post a Comment