[racket] A `check-expansion` for rackunit to test macros?

From: Tobias Hammer (tobias.hammer at dlr.de)
Date: Tue Aug 27 03:21:09 EDT 2013

expand-once with a datum argument uses (datum->syntax #f datum) on its
argument, i.e. the resulting syntax-object has no lexical context. That
means in this context if-let is unknown, therefore the expansion must fail.

I think passing in syntax with proper context is the right choice.

To [1]: DrRacket 5.3.4 fails too, other versions not tested. Have you  
enabled to run the test submod?

I don't know if its needed, as its use is limited to only very simple  
macros or otherwise writing down the expansion becomes pretty complex. But  
something to test if macros expansions raise certain errors would be neat.


PS: I you really want to expand a datum, set the namespace as in the  
sample code for expand

On Mon, 26 Aug 2013 16:45:19 +0200, Greg Hendershott
<greghendershott at gmail.com> wrote:

> I recently got a pull request where someone defined a
> `check-expansion` form to use with rackunit. Simplified excerpt:
> #lang racket/base
> (require syntax/parse/define)
> (define-simple-macro (if-let [binding:id value:expr] then:expr else:expr)
>   (let ([binding value])
>     (if binding then else)))
> (module+ test
>   (require rackunit)
>   (define (check-expansion input expected-output)
>     (check-equal? (syntax->datum (expand-once input)) expected-output))
>   (check-expansion '(if-let [x #t] 0 1)   '(let [(x #t)] (if x 0 1))))
> Although DrRacket doesn't warn that the the test fails, it fails with
> `raco test`:
> raco test conditionals.rkt
> raco test: (submod "conditionals.rkt" test)
> if-let: unbound identifier;
>  also, no #%app syntax transformer is bound
>   at: if-let
>   in: (if-let (x #t) 0 1)
>   context...:
>    /Users/greg/src/scheme/collects/rackjure/rackjure/conditionals.rkt:11:2:
> check-expansion
>    /Users/greg/src/scheme/collects/rackjure/rackjure/conditionals.rkt:
> [running body]
>    /Applications/Racket_v5.3.5/collects/compiler/commands/test.rkt:29:10:
> for-loop
>    f8
>    /Applications/Racket_v5.3.5/collects/compiler/commands/test.rkt:
> [running body]
>    /Applications/Racket_v5.3.5/collects/raco/raco.rkt: [running body]
>    /Applications/Racket_v5.3.5/collects/raco/main.rkt: [running body]
> Without giving it much time or deep thought, I came up with this
> version which succeeds in both DrRacket and raco test:
> (module+ test
>   (require rackunit)
>   (define (check-expansion input expected-output)
>     (check-equal? (syntax->datum (expand-once input))
>                   (syntax->datum expected-output)))
>   (check-expansion #'(if-let [x #t] 0 1)   #'(let [(x #t)] (if x 0 1))))
> In light of that:
> [1] Why don't DrRacket and `raco test` both succeed for the first  
> version?
> [2] Is my new version correct?
> [3] Could/should rackunit provide something like `check-expansion`?
> ____________________
>   Racket Users list:
>   http://lists.racket-lang.org/users

Tobias Hammer
DLR / Robotics and Mechatronics Center (RMC)
Muenchner Str. 20, D-82234 Wessling
Tel.: 08153/28-1487
Mail: tobias.hammer at dlr.de

Posted on the users mailing list.