[racket] macro not getting expanded as argument to class/c

From: Christopher (ultimatemacfanatic at gmail.com)
Date: Sun Sep 22 10:45:26 EDT 2013


You were right. I just needed to do the work of make-button-contracts in my define-sequencer-buttons-info-class syntax transformer and that saved me having to "recognize 'clause transformers', invoke the macro transformer on them, [and] simulate some of the aspects of normal macro transformation".

In case you or anyone following this is curious, the fixed code (with some previously omitted lines from the last email put back in) follows below.

Thanks to you Carl and to all participants on this list that make it such a great place to get help.


; make-button-contracts and its previously unlisted companion have been removed, as their function is subsumed in the macro below

(define-syntax [define-sequencer-buttons-info-class stx]
  (syntax-case stx []
    [[_ cls-name button ...]
      (let [[buttons
             (for/list [[btn-id {in-list {syntax->list #'[button ...]}}]]
                 {string-append {symbol->string {syntax->datum btn-id}} 
                                "-btn" }}
                btn-id })]]
        (when {null? buttons}
           "must supply at least one button name after class name" })
        (with-syntax [[[btn-name ...] buttons]]
          #'(define/contract cls-name
                 [btn-name {is-a?/c button-info%}] ... ] )
              (class object%
                (init-field-accessor btn-name ...)
                (super-new) ))))]))

On Sep 21, 2013, at 12:26 PM, Carl Eastlund <cce at ccs.neu.edu> wrote:

> Christopher,
> Ordinary macros are used to extend expressions and definitions; the arguments to class/c are neither, so they are not macro expanded.  There are ways to extend other kinds of things with macros -- match expanders, require transformers, provide transformers, and other things like them provide alternate kinds of transformers for syntactic forms other than expressions and definitions.  (Hypothetically, we could have used ordinary transformers for them as well, but that's a design question for another day.)
> If you want class/c* to use macro expansion for its clauses, you'll need to set it up yourself: recognize "clause transformers", invoke the macro transformer on them, simulate some of the aspects of normal macro transformation (e.g. applying a unique mark before and after), and so on.
> I don't suggest this, though.  It doesn't seem necessary.  Just do the work of make-button-contracts in the macro that constructs the call to class/c*, and there won't be any nested-expansion to do.  Adding a new kind of macro expander is probably overkill for the problem at hand.
> Carl Eastlund
> On Sat, Sep 21, 2013 at 2:15 PM, Christopher <ultimatemacfanatic at gmail.com> wrote:
> Hello, all you wonderful people. I'm seeing some strange behavior from my macros.
> So I invoke my define-sequencer-buttons-info-class macro with a bunch of identifiers.
> Its job is to rename the identifiers to add a "-btn" suffix (yes, I use macros to save typing tedium) and then
> invoke my make-button-contracts macro inside of my class/c* macro. The job of the former is to expand
> into a literal form that starts with init-field-accessor with some extra arguments which is interpreted specially by the latter. 
> When I can get make-button-contracts to be invoked, it runs correctly, expanding into an argument list starting with init-field-accessor.
> class/c* (star at the end) is my macro that expands argument pairs starting with literal symbols init-field-accessor and field-accessor 
> into sets of init, field, and method definitions for mutations of a given name---or passes regular class/c statements through unmodified.
> The problem is that when Racket is in the define-sequencer-buttons-info-class transformer procedure, it constructs a definition including
> an invocation of make-button-contracts nested inside my invocation of class/c*. When class/c* then gets expanded, it passes the 
> invocation of make-button-contracts through to the standard class/c (without the star)---unmodified----and then starts expanding class/c 
> without having first expanded make-button-contracts. 
> It further seems, that class/c does not macro-expand any of its arguments, but only matches literal names, so how am I going to get my make-button-contracts to expand
> BEFORE class/c* is expanded? How can I do this with Racket?
> Finally, lest anyone be confused. I use a convention where my s-expression lists that represent immediate procedure applications at that point in the source are surrounded with braces, the ones that are expanded to macros or core, non-procedure functionality are in parentheses, and the rest are in brackets. It's unconventional, but it works for me.
> Many thanks!
> ---Christopher
> ; module total-ca-state.rkt (main file) [ file abbreviated to relevant portions ]
> (define-syntax [make-button-contracts stx]
>   (syntax-case stx []
>     [[_ btn-name ...]
>       (let [[ret #'(init-field-accessor [btn-name {is-a?/c button-info%}] ...)]]
>         {print ret}
>         ret )]))
> (define-syntax [define-sequencer-buttons-info-class stx]
>   (syntax-case stx []
>     [[_ name button ...]
>       (let [[buttons 
>              (for/list [[btn-id {in-list {syntax->list #'[button ...]}}]]
>                {datum->syntax 
>                  btn-id
>                  {string->symbol
>                    {string-append
>                      {symbol->string {syntax->datum btn-id}} 
>                      "-btn" }}
>                  btn-id
>                  btn-id })]]
>         (define ret
>           #`(define name
>               (class/c*
>                 (make-button-contracts #, at buttons) )))
>         {print ret} 
>         ret )]))
> (provide sequencer-buttons-info%)
> (define-sequencer-buttons-info-class sequencer-buttons-info%
>   next-user
>   previous-user
>   ; ... long list of names (abbreviated) ...
>   lock-user
>   )
> ;  module class-util.rkt (required by the other file) [ truncated to relevant portions ]
> (define-syntax [class/c* stx]
>   (define [flatten lst [times 1]]
>     (if {zero? times}
>         lst
>         {flatten {apply append lst} {sub1 times}} ))
>   (syntax-case stx []
>     [[_ clause ...]
>      (let []
>        (define clause-list {syntax->list #'[clause ...]})
>        (when {zero? {length clause-list}}
>          {raise-syntax-error #f
>                              "must supply at least one clause"
>                              stx })
>        (define syntax-ret
>          {datum->syntax
>           stx
>           {append
>            {list #'class/c}
>            {flatten
>             (for/list [[clause {in-list clause-list}]]
>               (syntax-case clause [init-field-accessor field-accessor]
>                 [[init-field-accessor sub-clause ...]
>                  {init-field-accessor* clause} ]
>                 [[field-accessor sub-clause ...]
>                  {field-accessor* clause} ]
>                 ; THIS IS WHERE THE CALL TO make-button-contracts IS PASSED THRU TO REGULAR class/c
>                 [whole-thing {list {list clause}}] ))   
>             2 }}
>           stx})
>        {print syntax-ret}
>        syntax-ret )]))
> ____________________
>   Racket Users list:
>   http://lists.racket-lang.org/users

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20130922/287c8283/attachment-0001.html>

Posted on the users mailing list.