[racket] module+ test... (require rackunit) results in unbound identifier when used in macro
Ah, good point. If that's a concern then you want to do it your way. My
way would depend on rackunit when running the main module.
Carl Eastlund
On Wed, Jan 8, 2014 at 7:36 PM, Scott Klarenbach <scott at pointyhat.ca> wrote:
> Would that affect the loading of rackunit during main vs test runs of the
> file? For example,
> >> racket file-that-uses-macro.rkt
> >> raco test file-that-uses-macro.rkt
>
> ;; will the *non-test *invocation now require rackunit due to the macro
> definition, where as the dual require inside of module+ would not?
>
> Thanks.
>
>
> On Wed, Jan 8, 2014 at 4:29 PM, Carl Eastlund <carl.eastlund at gmail.com>wrote:
>
>> That's a good solution. I can suggest one that saves you having to add
>> two requires each time you use the macro, although it's a small
>> optimization: simply add (require rackunit) to the top of the file where
>> you define the macro, entirely outside the macro definition. Then you
>> don't need the extra (require rackunit) inside the macro template; you'll
>> be able to refer to test-case from rackunit the same way you referred to
>> require and provide from racket.
>>
>> Carl Eastlund
>>
>>
>> On Wed, Jan 8, 2014 at 7:13 PM, Scott Klarenbach <scott at pointyhat.ca>wrote:
>>
>>> 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.eastlund at gmail.com>wrote:
>>>
>>>> On Wed, Jan 8, 2014 at 6:44 PM, Scott Klarenbach <scott at 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.eastlund at 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" <stchang at 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 <scott at 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 scott at 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 scott at 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 scott at 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 scott at pointyhat.ca
> 200-1575 W. Georgia
> Vancouver, BC V6G2V3
>
> _______________________________________
> To iterate is human; to recur, divine
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20140109/a021c564/attachment-0001.html>