guile-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

News>> gmane.lisp.guile.devel: Re: syntax-local-value [racket]


From: Eli Barzilay
Subject: News>> gmane.lisp.guile.devel: Re: syntax-local-value [racket]
Date: Fri, 6 Jan 2012 21:26:21 -0500

6 hours ago, Andy Wingo wrote:
> 
> > syntax-local-value
> >
> > e.g.
> > http://docs.racket-lang.org/reference/stxtrans.html#%28def._%28%28quote._~23~25kernel%29._syntax-local-value%29%29
> 
> We don't have this.  It wouldn't be too hard to implement though;
> want to give it a try?
> 
> What is it used for, though?

It is used to get the value of a macro-level binding.  Usually that's
not very useful since you'd prefer to just use the macro (by producing
code that has it).  You could probably implement your own macro
expander which would use this to get the transformer functions and
call them directly but that's not a good use case either.  But there
are cases where you want to keep around syntax-level information to be
used *by* macros rather than being a user macro directly.  Probably
the most obvious example of that is struct definition: when you define
a struct, racket puts information about the struct on its name, so it
can be used by declarations of structs that inherit from it:

  -> (struct foo (x y z))
  -> foo
  #<procedure:foo>
  -> (begin-for-syntax (printf ">>> ~s\n" (syntax-local-value #'foo)))
  >>> #<procedure:self-ctor-checked-struct-info>
  -> (require (for-syntax racket/struct-info))
  -> (begin-for-syntax (printf ">>> ~s\n" (extract-struct-info 
(syntax-local-value #'foo))))
  >>> (#<syntax:1:8 struct:foo> #<syntax:1:8 foo> #<syntax:1:8 foo?> 
(#<syntax:1:8 foo-z> #<syntax:1:8 foo-y> #<syntax:1:8 foo-x>) (#f #f #f) #t)

(IIRC, the reason for the "checked" is to avoid faking these things,
which is needed for enforcing typed racket's assumptions.)

There are lots of such examples where you want to keep syntax
information around.

Related to this, we have `syntax-parameter-value' which is even more
exotic.  It is actually something that I didn't know about until I
worked on the syntax parameters paper, and all of the (very few) uses
of it that we have are using syntax paremeters as values (as above)
that obeys lexical scope.

But there was one reviewer that noted that a possible problem with
syntax parameters is that a macro that uses one essentially gets the
parameter macro to be part of its public interface, which can "leak"
out to other macros that use it.  An example from the paper is when
you have a `ten-times' macro:

  (define-syntax ten-times
    (syntax-rules ()
      [(_ body ...)
       (let loop ([n 10])
         (when (> n 0) body ... (loop (- n 1))))]))

and user code that uses it:

  (forever (ten-times (display "hey\n") (abort)))

Now the macro author decides that it's better to implement `ten-times'
via `forever', perhaps because that's the preferred primitive way to
loop:

  ;; (forever expr ...) is defined as a macro that loops forever, uses
  ;; an `abort' syntax parameter to make it possible to exit the loop

  (define-syntax ten-times
    (syntax-rules ()
      [(_ body ...)
       (let ([n 10])
         (forever body ...
                  (set! n (- n 1))
                  (when (= n 0) (abort))))]))

and now the above user code fails -- because the `abort' is shadowed
by `ten-times' which means that it aborts the wrong loop.

Obviously, this is a problem -- and perhaps a little less obvious is
that it's actually the *desired* behavior since the intention was that
`abort' is usable in `forever' forms including macros that use it.
This is all -- still -- in complete analogy with parameters, where you
can run into the same situation with a function that shadows something
like `current-output-port' and now functions that are called see the
shadowed value rather than the original one.

  (define (foo some-function)
    (parameterize ([current-output-port ...something...])
      (printf "something")
      (some-function)))

In some cases like the above you can just move the call to outside the
`parameterize', but in other cases that's not possible (with nested
functions etc).  In these cases, the solution in the parameter case is
to save the original value and restore it around calls to other
functions:

  (define (foo some-function)
    (define orig-stdout (current-output-port))
    (parameterize ([current-output-port ...something...])
      (printf "something")
      (parameterize ([current-output-port orig-stdout])
        (some-function))))

When I thought about this, I was pleasantly surprised that
`syntax-parameter-value' could be used in exactly the same way.

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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]