1
votes

I have a macro that extends the racket syntax, and at some point accepts a sequence of bog standard racket expressions. This looks something like this, the relevant syntax variable being body:

(syntax-parse stx
  [(_ some-id:id
      body:expr ...+)

This macro generates a racket class with a generated method like so:

#'(<class stuff>
   (define/public (some-id some-formal-parameter)
     body ...)

As I said the body is plain racket code, except for one expression that can be used exclusively in the body, for example:

(define-syntax-rule (tweet identifier value)
  (send this publish-tweet (quote identifier) value))

But this does not allow me to use some-formal-parameter because it is not defined. Is there some proper way in which I can define something that can exclusively be used in the body, and can still bind to variables in the context after expansion? Maybe via a splicing syntax class? Reusability is a big bonus, since this "type of body" may exists in multiple (similar) macros.

Some code for testing:

#lang racket
(require (for-syntax syntax/parse))

(define-syntax (define-something stx)
  (syntax-parse stx
    [(_ some-id:id
        body:expr ...+)
     #'(define some-id
         (new
          (class object%
            (super-new)
            (define/public (displ arg)
              (displayln arg))
            (define/public (tick some-formal-parameter) 
              body ...))))]))

(define-syntax-rule (tweet value)
  (send this displ value))


(define-something derp
  (define a 'not-derp)
  (tweet a))

(send derp tick 'derp)
1
I don't fully understand your question, but from what I can understand, it sounds like syntax parameters is the way to implement it. If you can post what your expected before-and-afters are, I can try to cook something up. :-)Chris Jester-Young
Yeah sorry about that. "Thinking with macros" is hard to get into. There is so much to learn, so much to know, so much you have to know. I am reading a paper about syntax parameters and it looks like this could solve it. I don't know if it solves it in a good way, but I will try some stuff out and report back :)Sam

1 Answers

1
votes

To reformulate the original question now that I know (and can answer) what I wanted to ask: how can I define a macro that can only be used in a certain context (for me: in the body of a method of a racket class), and how can I use dynamically bound variables. In the original code above: when using the expression (tweet a) not only do I want the value of a, but also the value of some-formal-parameter which is bound in the context of the code where the tweet macro is expanded (not where it is defined).

Chris Jester-Young kindly pointed me to syntax parameters, which indeed seem to solve both the issue of dynamic binding and "can only be used in certain contexts". A paper by Eli Barzilay, Ryan Culpepper, and Matthew Flatt helped me understand syntax parameters.

With respect to the original example code I posted, this is the solution I have come up with:

#lang racket

(require
  racket/stxparam
  (for-syntax syntax/parse))

(define-syntax-parameter tweet
  (lambda (stx)
    (raise-syntax-error 'tweet "use of an actor keyword outside of the body of an actor" stx)))

(define-syntax (define-something stx)
  (syntax-parse stx
    [(_ some-id:id
        body:expr ...+)
     #'(define some-id
         (new
          (class object%
            (super-new)

            (define/public (tick some-formal-parameter)
              (syntax-parameterize
               ([tweet
                 (syntax-rules ()
                   [(_ value)
                    (begin (displayln some-formal-parameter)
                           (displayln value))])])
               body ...)
              ))))]))

(define-something derp
  (define a 'not-derp)
  (tweet a))

(send derp tick 'derp)

The three key points of attention are the following.

  1. Because of the definition of the tweet macro, whenever it is used outside of the context of a syntax-parametrize statement (that changes the definition of tweet) it will throw an appropriate error.
  2. In the body of the public method tick of our class we change thedefinition of tweet to a macro that matches a pattern of the form (_ value) (which is the value we supply to tweet)
  3. The tweet macro can expand to something that both uses the bound value value, and the value of some-formal-parameter, whatever that may be.

I do not know if this is the proper way to deal with such a situation, but it seems good.