Inspired by a comment thread on a related question regarding functions instead of macros.
Is there any way to extend a Scheme syntax definition so that it can use the previous definition of the syntax in the new definition? Furthermore, this must be extensible, that is, it must be possible to chain the technique together several times.
For example, say we want to extend lambda
so that every time a function defined with lambda
is called, it prints "foo" before executing the function body. We can do this in the following way:
(define-syntax old-lambda lambda)
(define-syntax lambda
(syntax-rules ()
((_ args body ...)
(old-lambda args (display "foo") body ...))))
We can also extend this in another way (say, by printing "bar") by doing the following:
(define-syntax old-lambda-2 lambda)
(define-syntax lambda
(syntax-rules ()
((_ args body ...)
(old-lambda-2 args (display "bar") body ...))))
The end result being that functions defined with our new lambda
will print "foo", then "bar" each time they are called.
However, besides polluting the namespace with lots of old-lambda-<x>
, this necessitates making a new old-lambda-<x>
at the source code level each time we do this; this cannot be automated since you can't, say, use gensym
in the syntax definition either. Therefore, there's no good way to make this extensible; the only plausible solution is naming each one old-lambda-print-foo
or something similar to disambiguate, which is obviously not a foolproof solution. (For an example of how this could fail, say two different parts of the code were to extend lambda
to print "foo"; naturally, they would both name it old-lambda-print-foo
, and voila! lambda
is now an infinite loop.) Therefore, it would be very nice if we were able to do this in a way which ideally:
old-lambda-<x>
Macro expansion is an integral part of eval and compile . Users can also expand macros at the REPL prompt via the expand REPL command; See Compile Commands. Macros can also be expanded programmatically, via macroexpand , but the details get a bit hairy for two reasons.
A macro is an automated input sequence that imitates keystrokes or mouse actions. A macro is typically used to replace a repetitive series of keyboard and mouse actions and used often in spreadsheets and word processing applications like MS Excel and MS Word. The file extension of a macro is commonly . MAC.
Macro is defined as something that covers a large amount, or is large in size. An example of macro is the study of the key driving aspects of an economy; macro economics. An example of macro is a very close up photograph of an ant; a macro photograph. adjective.
Many of R's standard functions are written in R, which makes it easy for users to follow the algorithmic choices made. R is highly extensible through the use of packages for specific functions and specific applications.
In Racket, you can do this with modules. You could create a module that re-exports the entire Racket language except for Racket's lambda
, and exports your new macro under the name lambda
. I'll show one way to arrange the code.
The foo-lambda
module defines and exports the foo-lambda
form, which creates procedures that print "foo\n" when applied.
(module foo-lambda racket
(define-syntax-rule (foo-lambda formals body ...)
(lambda formals (displayln "foo") body ...))
(provide foo-lambda))
The racket-with-foo-lambda
module re-exports the entire Racket language except it provides foo-lambda
under the name lambda
.
(module racket-with-foo-lambda racket
(require 'foo-lambda)
(provide (except-out (all-from-out racket) lambda)
(rename-out [foo-lambda lambda])))
Now you can write a module in this "new language":
(module some-program 'racket-with-foo-lambda
(define f (lambda (x) x))
(f 2))
(require 'some-program)
Note that this doesn't change the Racket version of lambda
, and other Racket forms still use the Racket lambda
binding. For example, if you rewrote the definition of f
above as (define (f x) x)
, then Racket's define
would expand into a use of Racket's lambda
, and you would not get the "foo" printout.
You can chain extensions: each extension is defined in a module that imports the previous version. For example, your bar-lambda
module would import the foo-lambda
module, and so on.
Racket does this internally, in fact. The compiler only understands lambda
with positional arguments, but the Racket language has a lambda
that supports both positional and keyword arguments. The implementation of the Racket language has a module that replaces the built-in lambda
and #%app
(implicitly used to handle function application syntax) with versions that handle keyword arguments.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With