Okay, I must be really daft. I'm trying to create a macro (part of a larger undertaking) that given a function, wraps it in its own submodule, then require
s it, so that functions defined thusly cannot mess around with surrounding terms, but that the rest of their host module can use them as normal. Perhaps this isn't a good idea, but humour me – I cannot see why it should be impossible.
I've got the following minimal code example, in two files.
tinylang.rkt:
(provide (all-defined-out))
(define-syntax (modularise stx)
(syntax-case stx ()
[(_ func)
(with-syntax ([mod (datum->syntax stx 'testmodule)]
[nm (datum->syntax stx 'working)]
)
#`(begin
(print (list "Defining module " 'mod))(newline)
(module mod racket
(provide nm)
(define nm func)
)
(require (for-syntax (submod "." mod))) ;; at least kills a syntax error
(require (for-template (submod "." mod))) ;; doesn't help
))]))
tinyimp.rkt:
#lang racket
(require "tinylang.rkt")
(modularise (lambda () (display "this works!")))
; the following line would make everything work!
;(require (submod "." testmodule))
(working)
I feel guilty about spamming this site with questions which I feel should be trivial for me to solve with the documentation, but it's still not clear for me what happens at what stage. If someone knows a good resource (book, lecture, paper) I'd be really happy to hear about it. I know about the Racket documentation, which is really extensive, but often I miss the crucial details in the explanations there.
Yes, macro-generated requires and provides are tricky. They key is the lexical scope of the require-spec (ie the (submod "." testmodule)
).
From the docs:
In require ... the generator of the require-spec determines the scope of the bindings.
In other words, the require-spec and the uses of required identifiers must have the same scope.
Here is a version of tinylang.rkt that will work:
#lang racket
(provide (all-defined-out))
(define-syntax (modularise stx)
(syntax-case stx ()
[(_ func)
(with-syntax ([require-spec (datum->syntax stx '(submod "." testmodule))])
#`(begin
(module testmodule racket
(provide working)
(define working func))
(require require-spec)))]))