On 09/23/2010 08:24 AM, Doug Williams wrote:
Consider the following macro that has a definitional macro, define-x,
that stores a function definition in a structure bound to a generated
name, and a macro that either calls that stored function or it does
something else. [I used a generated name, x:<name>, because I need to
use the original for another purpose - illustrated here by just calling
the stored function.]

#lang racket

(struct x-info (name func))

(define-syntax (define-x stx)
   (syntax-case stx ()
     ((define-x (name . parameters) . body)
      (with-syntax ((id (datum->syntax
                         (syntax define-x)
                         (string->symbol
                          (format "x:~a" (syntax->datum #'name))))))
        #'(define-values (id name)
            (let ((func (lambda parameters . body)))
              (values (x-info 'name func) func)))))))

(define-syntax (do-x stx)
   (syntax-case stx ()
     ((do-x (name . arguments))
      (with-syntax ((id (datum->syntax
                         (syntax define-x)
                         (string->symbol
                          (format "x:~a" (syntax->datum #'name))))))
        (if (identifier-binding #'id)
            #'((x-info-func id) . arguments)
            #'(name . arguments))))))

(provide (all-defined-out))

Now, I reference this in a separate module:

#lang racket

(require "x-2.ss")

(define (dummy a b c)
   (printf "dummy: ~a ~a ~a~n" a b c))

(define-x (my-x a b c)
   (printf "my-x: ~a ~a ~a~n" a b c))

(do-x (my-x 1 2 3))
(my-x 1 2 3)
(do-x (dummy 1 2 3))

And, when I run it I get the desired results.

my-x: 1 2 3
my-x: 1 2 3
dummy: 1 2 3

Now, the question is whether this is the 'right' way to do this and are
there any 'gotchas' that I'm not aware of? Is there something better to
use than relying on identifier-binding to return #f when there is no
binding? And, whatever obvious questions I don't even know enough to ask.

One thing I'd worry about is someone providing <name> from a module without providing x:<name>. A solution to this problem is to have a single public name and change its meaning depending on the context.

One way to do that is to make x-info be an applicable struct:

  (struct x-info (name func)
    #:property prop:procedure
               (lambda (x . args)
                 (apply (x-info-func x) args)))

Then if you define my-x as an x-info, the struct itself handles this case:

  (my-x 1 2 3)

and do-x can behave differently when given something that (dynamically) answers true to x-info?.

Using identifier-binding is fragile in this case. In particular, even if a binding exists, it doesn't guarantee that it's an x-info value. A better way to encode the idea "my-x was defined using define-x" is to bind my-x to compile-time information using define-syntax. That's how structs work, for example: the struct name is bound to a record describing the struct, but the record also acts as a macro transformer to handle the syntax of constructor application.

  (begin-for-syntax
    (struct x-info (name func-id)
      #:property prop:procedure
                 (lambda (x stx)
                   (syntax-case stx ()
                     [(_ arg ...)
                      (with-syntax ([func-id (x-info-func-id x)])
                        #'(func-id arg ...))]))))

Now define-x can use a hygiene-generated name for the procedure, since it will only be accessed by going through the x-info name.

  (define-x (my-x x y z) ___)
  =>
  (begin (define (func x y z) ___)
         (define-syntax my-x
           (x-info 'my-x (quote-syntax func))))

Finally, do-x uses syntax-local-value to see whether my-x is bound to compile-time information and, if so, whether that information is specifically an x-info.

  (define-syntax (do-x stx)
   (syntax-case stx ()
     [(do-x (name . arguments))
      (let ([v (syntax-local-value #'name (lambda () #f))])
        (if (x-info? v) ___ ___))]))

In the actually application, I am using syntax-parse to parse a rather
more complicated syntax. Is there any way in syntax-parse I can define a
syntax class that would recognize a class of identifier based on their
bindings?

If you use the compile-time information approach I sketched above, yes. The 'static' syntax class recognizes identifiers statically bound to compile-time information matching a predicate that you supply.

There's an example of 'static' in the implementation of unstable/class-iop. See unstable/private/class-iop-ct first for the struct definition and syntax class definition ('static-interface'). Then look at 'define-interface' and 'send:' in unstable/class-iop for how static interfaces are defined and used.

Ryan
_________________________________________________
 For list-related administrative tasks:
 http://lists.racket-lang.org/listinfo/users

Reply via email to