[racket] Require macros

From: Ryan Culpepper (ryanc at ccs.neu.edu)
Date: Tue Jan 11 17:38:38 EST 2011

On 01/11/2011 09:08 AM, Eduardo Bellani wrote:
> Hello list.
> I am having a bit of a pain with trying to create a custom require
> syntax. I am trying to save some typing in an MVC like application by
> building some macros to recognize in the test files where the files to
> be tested are.
> I have 2 doubts I think, one is how to retrieve the module name for the
> my-model-test macro and the other is how to make those macros work in
> in the require form as the target-test.rkt file tries to do.
> BTW, I have looked at the require Macros section of the docs, but so far
> to no avail.
> For illustration, here is my (broken) code:
> ;; auxiliary.rkt
> #lang racket
> (require rackunit
>           racket/require-syntax
>           rackunit/text-ui)
> (provide (all-from-out rackunit)
>           (all-from-out rackunit/text-ui)
>           models-test
>           my-model-test)
> (define-syntax my-model-test
>    (syntax-rules ()
>      [(_)
>       (string-append "../app/model/" *MODULE-NAME*)]))
> (define-syntax models-test
>    (syntax-rules ()
>      [(_ model-name ...)
>       (values (string-append "../app/model/" model-name) ...)]))
> ;;target-test.rkt
> (require "../auxiliary.rkt"
>           (model-test "a-model")
>           (my-model-test))
> Any ideas?

First, you need to use define-require-syntax:

   (define-require-syntax from-model ___)

Next, since you want to compute the new path at compile time, you can't 
use syntax-rules. If you do, then you'll return the string-append 
expression as if it were a require form; but it isn't, and you'll get an 

   (define-require-syntax (from-model stx)
     (syntax-case stx ()
       [(from-model relpath)
        (string? (syntax-e #'relpath))  ;; only relative path, ie strings

Finally, you want to append the given string to a common prefix:

   (string-append "../app/model/" (syntax-e #'relpath))

But that's a string, and your require macro must return a syntax object. 
And since require is essentially a non-hygienic binding form that binds 
names based on the lexical context of the require subforms, you get the 
lexical context right. The right lexical context in this case is the 
same as the original require form, stx:

   (datum->syntax stx
                  (string-append "../app/model/" (syntax-e #'relpath)))

Then use it thus:

   (require (from-model "some-file.rkt")
            (from-model "another-file.rkt"))


Posted on the users mailing list.