[R6RS] Fresh syntax

Anton van Straaten anton at appsolutions.com
Wed May 10 05:27:52 EDT 2006


Re section 5.8, "Fresh syntax", in the syntax-case draft:

 > Should we consider instead a variant of 'syntax', say 'fresh-syntax',
 > that applies a unique mark to its output?  Should we consider
 > something more general, like MzScheme's 'make-syntax-introducer',
 > which creates a procedure that applies the same mark to a syntax
 > object each time it is applied?  Either can be used to define
 > 'generate-temporaries', which can then be considered a derived
 > procedure.

I'm not sure that either of these approaches will address the issue all 
that well.  To illustrate, here's an example adapted from SRFI 72 and 
modified to run under MzScheme, using MzScheme's 
'syntax-local-introduce'.  (It can also be written to use 
'make-syntax-introducer', which doesn't make any difference for my 
purposes.)

(define-syntax main
   (lambda (stx)
     (let ((make-swap
            (lambda (x y)
              (with-syntax ((x (syntax-local-introduce x))
                            (y (syntax-local-introduce y)))
                (syntax-local-introduce
                 (syntax
                  (let ((t x))
                    (set! x y)
                    (set! y t))))))))
       (syntax-case stx ()
         ((_)
          (with-syntax ((swap (make-swap (syntax s) (syntax t))))
            (syntax
              (let ((s 1) (t 2))
                swap
                (list s t)))))))))

(main)  ;=> (2 1)

This gives the expected result.  If the uses of syntax-local-introduce 
are removed, the result changes to (1 2), which is not what is intended. 
  To achieve the intended result, some ability to control the marks 
introduced by make-swap is needed.

However, doing this using procedures like syntax-local-introduce or 
make-syntax-introducer requires reasoning about mark introduction and 
cancellation, which can be tedious even in trivial cases such as the above.

(Aside: this example illustrates why something like an operational 
description in terms of marks is needed: I don't think there's any way 
to reason about the above code without understanding how marks work.)

I don't think a FRESH-SYNTAX form would eliminate this issue, i.e. 
reasoning about when and how to use FRESH-SYNTAX wouldn't be any easier. 
   Besides, something more than FRESH-SYNTAX alone would be needed, 
since FRESH-SYNTAX wouldn't directly provide a way to apply fresh marks 
to x and y, which are already syntax objects.

The overall problem is that the same marks need to be applied to more 
than one syntax object in a given scope.  A solution using a 
WITH-FRESH-SYNTAX form might be more tractable, e.g.:

   (lambda (x y)
     (with-fresh-syntax ((x x) (y y))
       (syntax
        (let ((t x))
          (set! x y)
          (set! y t))))))))

This would transform into something equivalent to:

   (lambda (x y)
     (let ((stx-tmp (make-syntax-introducer)))
       (with-syntax ((x (stx-tmp x)) (y (stx-tmp y)))
         (stx-tmp
          (syntax
           (let ((t x))
             (set! x y)
             (set! y t)))))))

I.e. all syntax objects introduced via the WITH-FRESH-SYNTAX bindings 
list, or via uses of the SYNTAX form within the WITH-FRESH-SYNTAX body, 
would have the same fresh mark applied.

I can't currently make any claim about the generality of this solution, 
but intuitively it seems as though something like this might be made to 
work.  It is intended to provide semantics similar to SRFI 72's 
QUASISYNTAX form, but in the context of traditional syntax-case.

Anton





More information about the R6RS mailing list