The absolute easiest (and probably best) way to do this would be to use rename-in
to import an identifier under a different name:
(require (rename-in racket/base [cons 😄]))
This is also the most direct way to do it, since it won’t create a syntax transformer at all—it will create a new binding that is identical to cons
in every way, including free-identifier=?
. From the compiler’s perspective, that makes cons
and 😄
indistinguishable.
However, this is a little bit magical. Another approach would be to manually create a rename transformer using make-rename-transformer
:
(define-syntax 😄 (make-rename-transformer #'cons))
If you can’t use rename-in
, using a rename transformer is the next best thing, since the free-identifier=?
function recognizes rename transformers specially, so (free-identifier=? #'cons #'😄)
will still be #t
. This is useful for macros that care about syntax bindings, since 😄
will be accepted in all the same places cons
would be.
Still, this is using some sort of primitive. If you really wanted to, how could you implement make-rename-transformer
yourself? Well, the key here is that macro application can appear in two forms in Racket. If you have a macro foo
, then it can be used in either of these ways:
foo
(foo ...)
The first case is an “id macro”, when a macro is used as a bare identifier, and the second is the more common case. If you want to write a macro that works just like an expression, though, you need to worry about both cases. You cannot do this with syntax-rules
, which doesn’t allow id macros, but you can do this with syntax-id-rules
:
(define-syntax 😄
(syntax-id-rules ()
[(_ . args) (cons . args)]
[_ cons]))
This pattern will make 😄
expand as expected in both scenarios. However, unlike the above two approaches, it will not cooperate with free-identifier=?
, since 😄
is now a whole new binding bound to an ordinary macro from the macroexpander’s perspective.
From that point of view, is make-rename-transformer
magical? Not really, but it is special in the sense that free-identifier=?
handles it as a special case. If you wanted to, you could implement your own make-rename-transformer
function and your own free-identifier=?
function to get similar behavior:
(begin-for-syntax
(struct my-rename-transformer (id-stx)
#:property prop:procedure
(λ (self stx)
(with-syntax ([id (my-rename-transformer-id-stx self)])
((set!-transformer-procedure
(syntax-id-rules ()
[(_ . args) (id . args)]
[_ id]))
stx))))
(define (my-rename-target id-stx)
(let ([val (syntax-local-value id-stx (λ () #f))])
(if (my-rename-transformer? val)
(my-rename-target (my-rename-transformer-id-stx val))
id-stx)))
(define (my-free-identifier=? id-a id-b)
(free-identifier=? (my-rename-target id-a)
(my-rename-target id-b))))
This will allow you to do this:
(define-syntax 😄 (my-rename-transformer #'cons))
…and (my-free-identifier=? #'😄 #'cons)
will be #t
. However, it’s not recommended that you actually do this, for obvious reasons: not only is it unnecessary, it won’t really work, given that other macros won’t use my-free-identifier=?
, just the ordinary free-identifier=?
. For that reason, it’s highly recommended that you just use make-rename-transformer
instead.
(define new-var old-var)
you do not introduce any operation at run-time. You are just defining that the same value has two different names, and you can use both of them without incurring in any inefficiency (you are just making your code more difficult to read). So ifold-var
is a function, by calling(new-var arguments)
is equivalent to call(old-var arguments)
, without any extra function call. – Renzo(define (de a d) (cons a d))
– Sylwester