[racket] help about a macro that uses a struct define with "make-struct-type"

From: Maurizio Giordano (maurizio.giorda at gmail.com)
Date: Fri Jul 29 12:44:18 EDT 2011

On Fri, 2011-07-29 at 12:20 -0400, Carl Eastlund wrote:
> On Fri, Jul 29, 2011 at 12:05 PM, Maurizio Giordano
> <maurizio.giorda at gmail.com> wrote:
> > Hi Carl,
> >
> > On Fri, 2011-07-29 at 11:09 -0400, Carl Eastlund wrote:
> >> Maurizio,
> >>
> >> The problem here may be that macros are expanded at a separate phase
> >> from the program's run-time.  Different phases have different stores,
> >> and thus different versions of structure definitions.  Your custom
> >> reader for the mset datatype produces an mset for use at run-time.
> >> The run-time print-type detects this kind of mset, but the
> >> compile-time print-type expects a different version of an mset.
> > Ok. the difference between the two phases is now clear to me...
> > what is not still clear is:
> > 1) at compile-time the "mset?" checker function is known... otherwise I
> > would see an "undefined mset?"
> > (in fact I specify a (require (for-syntax multiset)), where multiset is
> > a module implementing the new datastructure).
> > 2) always at compile-time, the (mset? < ... >) expression returns #f.
> > So it seems to me that at compile-time the struct object has lost some
> > information.
> There is one module called "multiset", but all of its definitions are
> executed twice because your program uses them in two different phases:
> once at run-time and once at compile-time.  That means
> there are two different mset types with two different constructors and
> two different mset? predicates.  Call the run-time versions mset.1 and
> mset?.1, and the compile-time versions mset.2 and mset?.2.  Similarly,
> you have print-type.1 and print-type.2 at run-time and compile-time.
> Your custom reader produces an mset.1 value.  Your macro calls
> print-type.2 which checks mset?.2, and mset?.2 produces #false when
> given an mset.1 value.  The code your macro produces, however, calls
> print-type.1, which checks mset?.1, and that produces #true (I'm using
> the new long-form names of #f and #t for readability).  So no
> information is being lost -- it's just that there are two versions of
> everything running around, and they cannot be mixed and matched.
Now it is perfectly clear to me.
thanks a lot.
> >> Prefab structures would fix this part of the issue, because they do
> >> not generate new types, they merely describe a subset of existing
> >> types.  However, you cannot add a custom reader or writer property to
> >> prefab types, so another part of your interface will break.
> > Yes, you are right... I would like to keep my custom reader/writer.
> > So I cannot use prefab structs.
> >>
> >> I do not know precisely what you need to detect this type at
> >> compile-time for, so I don't have any advice on how to handle this for
> >> your purposes.  I hope I have at least clarified the behavior you see.
> >
> > At compile-time I would like to process the macro inputs (of course also
> > <...> structs) to generate some pattern-matching "templates" to be
> > injected in the code the macro generates and execute at runtime.
> > Something like this:
> >
> > (define-syntax (mm stx)
> >  (syntax-case stx ()
> >     [(_ input ...)
> >         (let* ((l1 (syntax '(input ...)))
> >                (templates (... process/and/recognize msets in l1 ...)))
> >           (datum->syntax stx
> >                         `(lambda (x) ,(inject-code patterns)) stx))]))
> >
> > Therefore, what I really need is that the new data structure is parsed
> > (and its type cecked) at compile time.
> >
> > Thanks,
> > Cheers,
> > Maurizio.
> Are you sure you want to put mset values in as literal syntax?  That
> leads to all sorts of odd behavior -- for instance, the contents of
> those msets will need to be syntax at compile-time if they are to
> contain expressions, but non-syntax at run-time if they are to contain
> useful values.  The expansion system will not do this plumbing for
> you.  You'll get a lot more "bang for your buck" if you instead just
> use a match expander as both a pattern to recognize msets and a
> constructor to build them.  This avoids all issues of crossing phase
> boundaries, because the compile-time code acts entirely by recognizing
> identifier bindings, and does not have to know about the
> representation of msets at all.
The "templates" I generate at compile-time in the macro are some sort of
a syntax tree:

An example:
the macro (three) inputs: < (1 x), 2 > x 3
will generate an hash like this:

#hash ((mset . #hash((list . #hash((integer . 1)
                                   (symbol . x)))
                     (symbol . x)))
       (symbol . x)
       (integer . 3))    

By using this nested hashtable, the "inject-code" function will
produce a (recursive) pattern matchers code to be included in the lambda
code generated by the macro. Symbols in the input are not evaluated,
they will are considered identifiers to be bound (and unified)
during the pattern matching. 

Therefore, I think I am not using the mset values at compile-time.
I simply parse the macro inputs to generate a sort of syntax tree
(i.e. the hashtable).

> --Carl

Posted on the users mailing list.