2.4 KiB
Modules and Macros
Racket’s module system cooperates closely with Racket’s macro system for
adding new syntactic forms to Racket. For example, in the same way that
importing racket/base
introduces syntax for require
and lambda
,
importing other modules can introduce new syntactic forms (in addition
to more traditional kinds of imports, such as functions or constants).
We introduce macros in more detail later, in [missing], but here’s a simple example of a module that defines a pattern-based macro:
(module noisy racket
(provide define-noisy)
(define-syntax-rule (define-noisy (id arg ...) body)
(define (id arg ...)
(show-arguments 'id (list arg ...))
body))
(define (show-arguments name args)
(printf "calling ~s with arguments ~e" name args)))
The define-noisy
binding provided by this module is a macro that acts
like define
for a function, but it causes each call to the function to
print the arguments that are provided to the function:
> (require 'noisy)
> (define-noisy (f x y)
(+ x y))
> (f 1 2)
calling f with arguments '(1 2)
3
Roughly, the define-noisy
form works by replacing
(define-noisy (f x y)
(+ x y))
with
(define (f x y)
(show-arguments 'f (list x y))
(+ x y))
Since show-arguments
isn’t provided by the noisy
module, however,
this literal textual replacement is not quite right. The actual
replacement correctly tracks the origin of identifiers like
show-arguments
, so they can refer to other definitions in the place
where the macro is defined—even if those identifiers are not available
at the place where the macro is used.
There’s more to the macro and module interaction than identifier
binding. The define-syntax-rule
form is itself a macro, and it expands
to compile-time code that implements the transformation from
define-noisy
into define
. The module system keeps track of which
code needs to run at compile and which needs to run normally, as
explained more in [missing] and [missing].