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

Syntactic extensions to Scheme (long message)




Just a few comments explaining a little about how syntax tables in MIT
Scheme currently work.

Scheme programs are translated into SCode (1) before either the evaluator
or the compiler touch them, thus macros do not have to be handled by
either but by this translator, which we call the syntaxer.

The syntaxer has one required argument (the expression to translate to
Scode), and two optional arguments.  One of them is the syntax table
to use in the translation.  The other is the syntax environment, which
is the environment in which to evaluate any expressions which must be
evaluated at syntax time, e.g. lambda expressions which result in
macro expanders.

If these arguments are not given explicitly, default values are used.
These default values can be manipulated with the procedures
current-syntax-table, set-current-syntax-table!, with-syntax-table,
and the analogous ones for syntax environments.

There are some operations defined on syntax tables:

(syntax-table-define <table> <symbol> <translator>)
(syntaxt-table-ref <table> <symbol>) -> <translator>

and some less interesting ones.

A translator is supposed to translate an s-expression into Scode, but
source to source macro facilities are also provided.  Some syntax time
syntactic sugar is also provided, thus

(define-syntax <name-of-special-form> <translator>)

Does a syntax-time syntax-table-define on the current syntax table,

(let-syntax ((<name> <translator>)...) . <expresssions>)

is analogous to LET, creating a new syntax table,

(using-syntax <syntax-table> . <expressions>)

is the syntax-time with-syntax-table, and

(macro <formals> . <body>)

is analogous to LAMBDA but "returns" a source level translator, similar
in effect to traditional Lisp Macros, except that they are expanded at
syntax time.

We do not provide "reader" syntax for obtaining translators or
denoting expansions, we instead invoke them explicitely to insure that
the correct expander is used.  Thus if WHILE is defined by

(define-syntax while
  (macro (pred . exps)
    `(let ((pred (lambda () ,pred))
	   (body (lambda () ,@exps)))
       (letrec ((loop
		 (lambda ()
		   (if (pred)
		       (begin (body) (loop))))))
	 (loop)))))

The following 2 definitions have different effects, depending on
whether WHILE is redefined

(define-syntax for
  (macro (index low high . exps)
    `(let ((,index ,low))
       (while (<= ,index ,high)		; Dynamic use of WHILE
	      ,@exps
	      (set! ,index (1+ ,index))))))

(define-syntax for
  (let ((while-xform			; The translator
	 (syntax-table-ref (current-syntax-table) 'WHILE)))
    (macro (index low high . exps)
      `(let ((,index ,low))
	 ,(while-xform			; Static use
	   `(while (<= ,index ,high)
		   ,@exps
		   (set! ,index (1+ ,index))))))))

Note that the traditional Lisp DEFMACRO can be obtained by

(define-syntax defmacro
  (macro (pattern . body)
    (let ((the-name (car pattern))
	  (the-translator `(macro ,(cdr pattern) ,@body)))
      `(begin
	(syntax-table-define (current-syntax-table)	; Runtime
			     ',the-name
			     ,the-translator)
	(define-syntax ,the-name ,the-translator)))))	; Syntax (compile) time

In the example that Jonathan gives we would also have problems, since
the default is that the current syntax environment is the same as the
read-eval-print loop environment, thus the code would "work"
interpreted but not compiled.  Changing current syntax environment is
a possibility which we should probably do.

We do not object to using Scheme for syntactic extensions.  As a
matter of fact, I never write syntactic expressions which do not end
up being macros (after grabbing the appropriate stuff), only the
bottom level (the core special forms) translate to Scode, but there is
no reason not to make this an option in our system.

I agree with Jonathan in that syntax should not be used for
optimizations, and also in that lambda-bound names should take
precedence over syntactic items.  Thus in his binding-of-IF example, I
think that '(1 2 3) should be returned.  We probably will change our
system to do this, but we have not yet done that.


(1) Scode can be thought of as a small, explicit version of Scheme,
where every expression is an explicit special form, thus it has
VARIABLE and COMBINATION explicit special forms.