[plt-scheme] create complex object @ compile time but made available @ run time?

From: Ryan Culpepper (ryanc at ccs.neu.edu)
Date: Wed Jun 13 10:20:55 EDT 2007

On Wed, 2007-06-13 at 10:42 +0200, Jens Axel Søgaard wrote:
> YC wrote:
> > 
> > On 6/13/07, *Jens Axel Søgaard* <jensaxel at soegaard.net 
> > <mailto:jensaxel at soegaard.net>> wrote:
> > 
> > 
> >      > The problem here is not that it is a complex object, but that
> >      > structure definitions are generative - it would have worked
> >      > if you used vectors. Back to the structure problem. To quote the
> >      > documentation:
> > 
> >      >   "Each time a define-struct expression is evaluated, a new structure
> >      >    type is created with distinct constructor, predicate,
> >     accessor, and
> >      >    mutator procedures."
> > 
> > 
> > Thanks for the great explanation Jens - this makes great sense - but is 
> > there a way to prevent struct being evaluated twice, once for (require) 
> > and once for (require-for-syntax)?  
> 
> The semantics of require and require-for-syntax demand that the
> required module is evaluated twice, so I'd be inclined to say no.
> But there might be an alternative route. See below.
> 
> > In the example you cited the double 
> > eval is clearly intended because each define-struct is enclosed 
> > lexcially and it makes sense that they are different structs (like 
> > enclosed lambda & variables are different), but IMHO 
> > (require-for-syntax) and (require) evaluate the same code twice is 
> > _surprising_ (I thought it's more like "importing" names to the 
> > transformation environment) - love to hear your and others' take on 
> > whether this is expected.
> 
> The imported names are bound to values. Somewhere and at some time
> these values must have been evaluated. To run a piece of code,
> it must be expanded first. Since macros and what they depend on live
> in the transformation environment, values and structure definitions
> are needed in the transformer environment before they are needed
> in the runtime environment. One could argue that the values in the
> transformer enviroment could be reused in the runtime environment
> (that's what the defmacro system does), but that leads to a number
> of problems. Problems that disappear, when the the runtime and the
> transformer environment are separated.
> 
>   >     One solution is to calculate the values at expansion time, and
> >     then expand to (create-foo val1 ...). 
> > 
> > This is how I currently have it (but my code have multiple nested struct 
> > creations, which makes it difficult to fully leverage expansion time) - 
> > until I thought - why not try to see if the object(s) can be generated 
> > in compile time and return to run time... close but not quite :)
> 
> *Maybe* the following technique can be used. The example features
> structures in structures. A square consists of four points. The
> macro (create-square n) calculates at expansion time the coordinates
> of the four corners of a square with side length 2*n (just to
> show an actual computation takes place).
> 
> [...]

The code you gave uses 'print-convert' (lib "pconvert.ss") to generate
the syntax that would recreate the structure at run-time. It will
usually work for simple structures. But here are some problems that you
may encounter if you use that method for interesting values:

First, 'print-convert' produces uncompilable code (containing '...')
when it can't convert a value. This happens for procedures, structs it
cannot inspect, etc.

Second, 'print-convert' leaves syntax objects unconverted and unquoted.
When you use 'datum->syntax-object' on an S-expression that contains
embedded syntax objects, it doesn't quote them with an "extra layer" of
syntaxness; it stops and leaves them intact. Here's what happens to the
following value on the 'print-convert' trip:

    (list #'(+ 1 2)) ;; a list containing a single syntax object
      --print-convert-->
    `(,#'(+ 1 2))
      --datum->syntax-object-->
    #'`(,(+ 1 2))
      --eval-->
    (list 3)

These effects can be truly baffling, especially if evaluating the
"expression" causes an error.

The only truly safe way I know of transferring values across phases is
to write your own 'X->X-syntax' serializer.

Ryan

> _________________________________________________
>   For list-related administrative tasks:
>   http://list.cs.brown.edu/mailman/listinfo/plt-scheme



Posted on the users mailing list.