[Top][All Lists]

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

Re: Internal defines

From: Tom Lord
Subject: Re: Internal defines
Date: Mon, 10 Nov 2003 19:11:18 -0800 (PST)

Dirk wrote:

  According to R5RS, the body of a form like lambda, let, let*
  etc. looks like follows:

  <body> --> <definition>* <sequence>
  <sequence> --> <command>* <expression>
  <command> --> <expression>
  <definition> --> (define <variable> <expression>)
  | (define (<variable> <def formals>) <body>)
  | (begin <definition>*)

  That is, it starts with zero or more definitions followed by one or
  more expressions.  The definition forms must start with the literal
  symbol 'define' or 'begin', which may enclose further definitions or
  begin forms. In any case, there is no mentioning of macro expansion

Dirk is wrong.   

(By striking coincidence, I recently spent about 6 hours figuring this
out myself.  In my personal experience, it is hella easy to be wrong
on this topic :-)

R5RS is not strikingly crystal clear on this matter, but it _does_
speak of macro expansion in this context and actually provides a
coherent and reasonable semantic.

The critical section is "5.3 Syntax definitions":

    Although macros may expand into definitions and syntax definitions
    in any context that permits them, it is an error for a definition
    or syntax definition to shadow a syntactic keyword whose meaning
    is needed to determine whether some form in the group of forms
    that contains the shadowing definition is in fact a definition,
    or, for internal definitions, is needed to determine the boundary
    between the group and the expressions that follow the group. For
    example, the following are errors:

Key here is "macros may expand into definitions and syntax definitions
in _any_ context that permits them" [emphasis added].

Thus, this is quite legal:

    ((foo (syntax-rules ()
            ((foo (proc args ...) body ...)
             (define proc
               (lambda (args ...)
                 body ...))))))
    (let ((x 3))
      (foo (plus x y) (+ x y))
      (define bar x)
      (plus bar x)))

  => 6

(which contradicts Dirk's reading).

On the other hand, as noted by the spec, this is "an error"

    ((foo (syntax-rules ()
            ((foo (proc args ...) body ...)
             (define proc
               (lambda (args ...)
                 body ...))))))
    (let ((x 3))
      (foo (plus x y) (+ x y))
      (define foo x)
      (plus foo x)))

because, looking at the contents of the LET, we can expand FOO and
realize that that is apparently a definition, but the subsequent
DEFINE of FOO undermines that interpretation since it's scope
encompasses the apparent macro-use of FOO (c.f. the "determining
boundaries" foo in the language from 5.3 quoted above).

Guile currently does not detect that error and simply returns 6 for
the second, error-laden, form.  Thus, Guile's conformance is saved in
this instance by the distinction between "is an error" and "an error
is signaled".

It is _slightly_ intimidating to think of what would be required of an
implementation, especially an SCM-derived implementation, to actually
detect that error.   Overall, Guile suffers from the sin of SCM -- of
not making expansion and evaluation separate phases.

Whether or not the unambiguous (but obscure) english language of 5.3
is born out by the "authoritative" denotational semantics is a
question I haven't investigated.   But I think it is crystal clear
(once the subtleties understodd) that if the denotational semantics do
not support 5.3 then the denotational semantics have a bug.

There _is_, denotational semantics notwithstanding, a quite clear
operational interpretation here in which the successive forms in the
"body" (not <body>) of the LET are expanded and then interpreted.
However, to actually detect and report the "an error", the operational
semantics has to include an observence of what head forms arise during
the expansion of forms that might or might not be part of the
<definition>s of the LET.


reply via email to

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