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

From: Carl Eastlund (cce at ccs.neu.edu)
Date: Fri Jul 29 12:51:17 EDT 2011

On Fri, Jul 29, 2011 at 12:44 PM, Maurizio Giordano
<maurizio.giorda at gmail.com> wrote:
> On Fri, 2011-07-29 at 12:20 -0400, Carl Eastlund wrote:
>> 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.
>> 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).

You are not using them as outputs, but you are using them as inputs.
Even that is problematic, exactly as you have seen.  I suggest writing
a macro using only standard syntax objects containing lists, strings,
symbols, and numbers for both input and output if at all possible.
This will save a lot of trouble.  Using hash tables in syntax objects
can work, but even that brings up some tricky issues.  I find it is
almost always better to consume and produce code that refers to or
produces complex data types, rather than code actually represented as
complex data types.


Posted on the users mailing list.