My macro works in the global namespace, but fails inside of a let

46 views
Skip to first unread message

Cistian Alejandro Alvarado Vázquez

unread,
Oct 6, 2019, 5:39:56 PM10/6/19
to Racket Users
I start with the following code, which works as you'd expect:

#lang racket

(require syntax/parse/define)

(define-simple-macro (my-macro)
  (display a_definition))

(define a_definition "some string")
(my-macro)

(It displays the text "some string")

Then tried defining a_definition and calling the macro again, but inside of let:

(require syntax/parse/define)

(define-simple-macro (my-macro)
  (display a_definition))

(let ()
  (define a_definition "some string")
  (my-macro))

This time I get an error:

a_definition: unbound identifier
  context...:
  other binding...:
  common scopes...: in: a_definition

!! This perplexes me, as from my (very limited) understanding, the above code should just macrologically expand into something akin to:

(let ()
  (define a_definition "some string")
  (display a_definition))

Which is what appears to be happening when I do the same thing in the global space. I really want to be able to define something within a code block and have access to that definition via macros called in that same code block. What is recommended?

gfb

unread,
Oct 6, 2019, 7:23:10 PM10/6/19
to Racket Users
No doubt you will soon get more precise answers, and help for your goal, from others, but the first step is that macros in scheme and racket are not straight textual substitutions.

The a_definition in the template of my_macro has much the same scope as it would if my_macro were just a function (defined with define, and then I'd refer to (display a_definition) as “the body” rather than “the template”).

gfb

unread,
Oct 6, 2019, 7:32:09 PM10/6/19
to Racket Users
Hmm, maybe you'd find “parameters” (not to be confused with function parameters) useful for your goals.

#lang racket

(define current_meaning (make-parameter "some string"))

(define (display_current_meaning)
  (display (current_meaning)))

(display_current_meaning)

(let ()
  (parameterize ([current_meaning "a different string"])
    (display_current_meaning)))

(display_current_meaning)

Simon Schlee

unread,
Oct 7, 2019, 9:04:56 AM10/7/19
to Racket Users
Your second macro does not work as you expect, because by default macros in racket are hygienic, described roughly that means that a macro only "manipulates" the syntax it is given as arguments, not with the syntax around its invocation. As gfb has mentioned one way to do this is to use parameters, this way you can achieve dynamic effects without breaking hygiene.


You say you want to use some definitions in your macro, why not just pass the relevant ones as parameters?
Without knowing more about what you are trying to do, it is difficult to suggest a good direction/solution.

Not part of the answer, but related and interesting:
(fifth RacketCon): Matthew Flatt — Bindings as Sets of Scopes

Simon Schlee

unread,
Oct 7, 2019, 9:27:15 AM10/7/19
to Racket Users
I meant to write (less confusing):
"You say you want to use some definitions in your macro, why not just pass the relevant ones as arguments to your macro?"

Cistian Alejandro Alvarado Vázquez

unread,
Oct 7, 2019, 10:33:26 AM10/7/19
to Racket Users
Okay, that I am aware of, but why is it hygienic inside of let but *not* in the global namespace? Also, you should (from my limited understanding!) still be able to access bindings from inside of a macro, no? For example, I can call functions inside of a macro and those identifiers are available even though they weren't provided as arguments. So it appears that macros have access to module-level bindings but NOT more local bindings, and that is what I'm confused about.

Eric Griffis

unread,
Oct 7, 2019, 11:27:40 AM10/7/19
to Cistian Alejandro Alvarado Vázquez, Racket Users
Hi Cistian,

Both of your examples are hygienic, in the sense that neither actually
do anything that violates macro hygiene.

When `a_definition` is defined in the module context (i.e., the
"global namespace"), it has the same scope as the `a_definition` used
inside `my-macro`. (There's more to it, but this is enough for now.)

When `a_definition` is defined in a local context (e.g., inside a
`(let () ...)` form) outside `my-macro`, it won't have the same scope
as the `a_definition` used inside `my-macro`.

There's an easy trick to get around this. As Simon suggested, make
`a_definition` an argument to `my-macro`:

(define-simple-macro (my-macro a_definition)
(display a_definition))

Now, each call to `my-macro` "borrows" `a_definition` from the macro
caller's scope. There are other ways to tackle the problem, but this
is the safest option.

Eric
> --
> You received this message because you are subscribed to the Google Groups "Racket Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to racket-users...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/47407fc2-2c51-4cf1-a5b7-78a390484fbf%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages