Sorry, gmail sent before I could paste :( ...
So I changed the macro definition to: (define-syntax (define/spec stx) (syntax-parse stx [(_ (proc:id arg:expr ...) body:expr ... #:doc contract:expr arg-spec:expr desc:expr #:test test-body:expr ...) (with-syntax ([id-str (symbol->string (syntax->datum #'proc))] [ru-require (format-id stx "~a" "rackunit")]) #'(begin (provide (proc-doc/names proc contract arg-spec desc)) (define (proc arg ...) body ...) (module+ test (require ru-require) (require rackunit) (test-case id-str test-body ...))))])) Notice the extra (require rackunit), after the modified (require ru-require). Is that the right solution? On Wed, Jan 8, 2014 at 4:00 PM, Carl Eastlund <carl.eastl...@gmail.com>wrote: > On Wed, Jan 8, 2014 at 6:44 PM, Scott Klarenbach <sc...@pointyhat.ca>wrote: > >> Thanks guys. >> >> I changed it to this: >> >> (define-syntax (define/test stx) >> (syntax-parse stx >> [(_ (proc:id arg:expr ...) >> body:expr ... >> #:test test-body:expr ...) >> (with-syntax ([id-str (symbol->string (syntax->datum #'proc))] >> [ru-require (format-id stx "~a" "rackunit")]) >> #'(begin >> (define (proc arg ...) body ...) >> (module+ test >> (require ru-require) >> (test-case id-str test-body ...))))])) >> >> and it works now. Is that what you meant Carl? >> > > Yep, that's it. > > >> The problem is that check-true (and check-false) are not in scope (as >>> you discovered) during expansion. >> >> >> I'm still a bit confused as to why. I figured the (require rackunit) >> would have brought check-true into scope during expansion, much the same as >> if I just copied the (begin... form into another file without the macro. >> > > The (require rackunit) does bring the names into scope... but _which_ > scope? That's always the important question with macros. By default, a > binding form brings names into the scope in which the binding occurrence of > the names are written. That's why if you write a local binding inside a > macro definition, only the code inside the macro can see it, while if you > bind a name that the user passed in, the user can see it. > > With require, neither party writes the name; the name shows up in the > module definition. Therefore, the require form has to figure out what > scope to bind the names in some other way. So, for module names at least, > require uses the scope in which the module name is written to bind all of > the definitions provided by the module. (Things get more interesting if > you use prefix-in or other import forms; they each have their own rules.) > > In your example, what scope was the module name rackunit written in? > Inside the macro definition. That's why writing #'(let ([check-true > <stuff>]) body ...) and#'(begin (require rackunit) body ...) both make > check-true visible only inside the macro definition. To make check-true > visible to the macro user, you have to change the context of the name > check-true. In the first case, that means changing the context of the > identifier check-true; in the second, it means changing the context of the > module name rackunit. > > So when things go wrong with macro bindings, the first question is always > "_which_ scope is this bound in?" and usually the second is "well, what > context does the binding occurrence have?". > > I hope that helps, these are really murky waters, so if you're confused, > you're definitely not alone. We're happy to ask questions frequently and > in detail. > > Good luck! > > >> On Wed, Jan 8, 2014 at 2:37 PM, Carl Eastlund >> <carl.eastl...@gmail.com>wrote: >> >>> I do not suggest rebinding the whole body like that; it could be >>> seriously problematic if the user refers to something not viable to the >>> macro definition. What you need to rebind is the name "rackunit" itself >>> passed to require so that it has the context of the macro application. >>> That way the names bound by the require will be visible to the macro user. >>> On Jan 8, 2014 5:29 PM, "Stephen Chang" <stch...@ccs.neu.edu> wrote: >>> >>>> The problem is that check-true (and check-false) are not in scope (as >>>> you discovered) during expansion. Here is one way to fix your problem >>>> by recapturing the test expressions to have the proper context. You >>>> can also use syntax-local-introduce instead of the lambda (someone >>>> correct me if this is not proper usage). >>>> >>>> #lang racket >>>> (require (for-syntax syntax/parse)) >>>> >>>> (define-syntax (define/test stx) >>>> (syntax-parse stx >>>> [(_ (id arg ...) body ... #:test test-body ...) >>>> (with-syntax ([id-str (symbol->string (syntax->datum #'id))] >>>> [(new-test-body ...) >>>> (map >>>> (λ (s) (datum->syntax #'here (syntax->datum s))) >>>> (syntax->list #'(test-body ...)))]) >>>> #'(begin >>>> (define (id arg ...) body ...) >>>> (print id-str) >>>> (module+ test >>>> (require rackunit) >>>> (test-case id-str >>>> new-test-body ...))))])) >>>> >>>> (define/test (my-fn a b c) >>>> (print a) >>>> (print b) >>>> (print c) >>>> #:test >>>> (check-true #t) >>>> (check-false #t)) >>>> >>>> On Wed, Jan 8, 2014 at 4:53 PM, Scott Klarenbach <sc...@pointyhat.ca> >>>> wrote: >>>> > I have the following macro: >>>> > >>>> > (define-syntax (define/test stx) >>>> > (syntax-parse stx >>>> > [(_ (id arg ...) body ... #:test test-body ...) >>>> > (with-syntax ([id-str (symbol->string (syntax->datum #'id))]) >>>> > #'(begin >>>> > (define (id arg ...) body ...) >>>> > (print id-str) >>>> > (module+ test >>>> > (require rackunit) >>>> > (test-case id-str >>>> > test-body ...))))])) >>>> > >>>> > Which handles some testing boilerplate and allows me to use it like >>>> so: >>>> > >>>> > (define/test (my-fn a b c) >>>> > (print a) >>>> > (print b) >>>> > (print c) >>>> > #:test >>>> > (check-true #t) >>>> > (check-false #t)) >>>> > >>>> > The problem is that the (require rackunit) expression inside of >>>> (module+ >>>> > test) is not being picked up after macro expansion. Running either >>>> the code >>>> > or the tests (via raco test) results in: unbound identifier >>>> check-true. >>>> > >>>> > Adding (module+ (require rackunit)) to the top of the file solves the >>>> > problem and both the file and tests run as expected. >>>> > >>>> > What am I missing? >>>> > >>>> > Thanks. >>>> > -- >>>> > Talk to you soon, >>>> > >>>> > Scott Klarenbach >>>> > >>>> > PointyHat Software Corp. >>>> > www.pointyhat.ca >>>> > p 604-568-4280 >>>> > e sc...@pointyhat.ca >>>> > 200-1575 W. Georgia >>>> > Vancouver, BC V6G2V3 >>>> > >>>> > _______________________________________ >>>> > To iterate is human; to recur, divine >>>> > >>>> > ____________________ >>>> > Racket Users list: >>>> > http://lists.racket-lang.org/users >>>> > >>>> >>>> ____________________ >>>> Racket Users list: >>>> http://lists.racket-lang.org/users >>>> >>> >> >> >> -- >> Talk to you soon, >> >> Scott Klarenbach >> >> PointyHat Software Corp. >> www.pointyhat.ca >> p 604-568-4280 >> e sc...@pointyhat.ca >> 200-1575 W. Georgia >> Vancouver, BC V6G2V3 >> >> _______________________________________ >> To iterate is human; to recur, divine >> > > -- Talk to you soon, Scott Klarenbach PointyHat Software Corp. www.pointyhat.ca p 604-568-4280 e sc...@pointyhat.ca 200-1575 W. Georgia Vancouver, BC V6G2V3 _______________________________________ To iterate is human; to recur, divine
____________________ Racket Users list: http://lists.racket-lang.org/users