[racket] module+ test... (require rackunit) results in unbound identifier when used in macro
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/20140108/98e2fe60/attachment-0001.html>