Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extensible macro definitions

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:

  • Doesn't require us to pollute the namespace with lots of old-lambda-<x>
  • Or, failing that, guarantees that we won't have collisions.
like image 742
Lily Chung Avatar asked May 27 '14 21:05

Lily Chung


People also ask

What is macro expansion?

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.

What is a macro simple definition?

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.

What is macro define with example?

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.

What does it mean when we say R is extensible?

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.


1 Answers

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.

like image 184
Ryan Culpepper Avatar answered Oct 06 '22 23:10

Ryan Culpepper