[plt-scheme] Attaching compile time information to identifiers

From: Eli Barzilay (eli at barzilay.org)
Date: Sun Oct 29 17:38:26 EST 2006

There is a problem with this approach though: because the information
is kept in a table that is in the syntax environment, it is not kept
around for multiple modules.  For example, this code (due to Matthew):

  (module p mzscheme
    (require m)
    (f one +))
 
  (module q mzscheme
    (require m p)
    (display (g one)))

wouldn't work.  There is another solution though -- you put the
syntax-level information as the value of a binding.  The thing is that
a transformer is just a procedure, so you can make a new struct type
that can be applied as a procedure, but also carries extra information
which is accessible for the transformer environment.  So, to make a
`foo' binding that has a runtime value and a syntax-time value, you
produce syntax that defines some `hidden-foo' as the actual run-time
expression, then create a macro procedure that always expands to
`hidden-foo', wrap that in the applicable struct type and hang the
syntactic information there.

I used this approach in my course plugin (if you want to see the code,
get it from csu660.barzilay.org/csu660.plt), which is a good sample of
what you can do with this:

  (define-type EXPR
    [Num (n number?)]
    [Add (l EXPR?) (r EXPR?)]
    [Sub (l EXPR?) (r EXPR?)])
  (define (eval expr)
    (cases expr
      [(Num n) n]
      [(Add l r) (+ (eval l) (eval r))]))

Note that unlike the EoPL language, `cases' does not require the type
identifier.  Instead, the `define-type' defines `Num' and `Add' as
above -- they can be used as a runtime value (constructor procedures),
and they can be used at syntax-time which is how `cases' knows what
type is expected there (so it can complain about a missing case).

[I think that Dave Herman came up with the same idea and put it in a
planet library.]


On Oct 29, Sam Tobin-Hochstadt wrote:
> What you probably want here is a `module-identifier-mapping', which is a
> hashtable keyed on identifiers, with the equality being
> `module-identifier=?'.  The transformer for `f' can add `id' to the map,
> and then `g' can recognize id by looking it up in the map.  
> 
> The following code demonstrates the idea:
> 
> (module m mzscheme
>   (require-for-syntax (lib "boundmap.ss" "syntax"))
>   (define-for-syntax mapping (make-module-identifier-mapping))
>   
>   (provide f g)
>   
>   (define-syntax (f stx)
>     (syntax-case stx ()
>       [(f id args ...)
>        (begin
>          (module-identifier-mapping-put! mapping #'id #'(args ...))
>          #'(void))]))
>   
>   (define-syntax (g stx)
>     (syntax-case stx ()
>       [(g id)
>        (module-identifier-mapping-get mapping #'id)])))
> 
> (module n mzscheme
>   (require m)
>   
>   (f one + 3 4)
>   (f two * 9 10)
>   
>   (display (g one))
>   (newline)
>   (display (g two)))

-- 
          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                  http://www.barzilay.org/                 Maze is Life!


Posted on the users mailing list.