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

Re: [Scheme-reports] R7RS syntax object questions

I believe the short answer to your questions is that macros and other special forms could be first-class objects--there is no semantic reason to forbid it--but that compiler writers would scream and run away in horror.

(To begin with, any _expression_ of the form ((car x) y ...) could be a macro call, and hence the arguments "y ..." cannot be evaluated until it is determined--at run time--that (car x) is in fact a function.  Furthermore, consider this case:

(let ((x (cons y z)))
  ((car h) (cdr x))
  (+ 4 (car x)))

(car h) could conceivably be a macro that would extract the "x" from the "(cdr x)" _expression_ and produce "(set-car! x 8)" or something like that.  Therefore, it would be unsound for the compiler to simply replace "(car x)" with "y".  --Calling functions from data structures, as in "((car fs) a b)", seems a rare thing to do, but now we must realize that global variables can usually be modified too, and if ((car mac-list) x) always gets the current car of the mac-list (as opposed to extracting the car once and assuming it'll always remain the same), it would be terribly inconsistent if (f x) did not get the current value of f and, if it happened to be a new macro, to expand some new code and run it.  Therefore, the compiler must assume that even "(f (cdr x))" could actually modify the car of x, if f might be redefined in between when this code is compiled and when it is run.  I expect real compiler writers could produce more common and compelling examples of important optimizations that become impermissible (or incredibly difficult) in the presence of first-class macros (loop invariants come to mind).

I believe it is nevertheless possible--using partial evaluation, rules about when threads learn of updated global variables, and a system of invalidating and recompiling functions--to have efficient compiled code and first-class macros, but I don't think such a system exists right now.)

You might find some Scheme or Lisp interpreters that provide macros and special forms as first-class objects--in fact, I have an Arc interpreter that does just this--but don't expect to find it in compilers, or language standards, any time soon.

--John Boyle
Science is what we understand well enough to explain to a computer. Art is everything else we do. --Knuth

On Thu, Apr 25, 2013 at 1:43 PM, Stephen Lewis <lewis+scheme@x> wrote:
In studying the draft report and the various implementations, I have formed
several questions on which I would like the opinion of an expert.
If this is the wrong place to ask such questions please suggest a more
appropriate forum.

In the R7RS standard, are macros (i.e syntax objects) considered to be
first-class objects?
[First-class object being an entity that can be constructed at run-time,
passed as a parameter, returned from a subroutine, or assigned to a variable.]

For example would these examples be considered to be standard compliant R7RS
Scheme? The implementations to which I have access do not all behave
in the same way.

Storing a syntax object in a variable?

(define myand and)

Calling my stored object as I can the original?

(myand #t (+ 1 2 3))

Returning a syntax object from a procedure?

(define (return-op) and)
(define myop (return-op))
(myop #t (+ 1 2 3))

May I call a syntax in the same way I call a primitive procedure?

(and #t (+ 1 2 3))
((return-op) #t (+ 1 2 3))
((and and) #t (+ 1 2 3))

I have read the R7RS draft and I am having difficulty answering these questions.
I find that there are "Primitive Expressions" and "Derived Expressions" and
that derived expressions "can instead be defined as macros" but this does not
seem to be a requirement. The description of a procedure call although in
the Primitive _expression_ section does not appear to exclude syntax objects.

"A procedure call is written by enclosing in parentheses an _expression_
for the procedure to be called followed by espressions for the arguments
to be passed to it."

An _expression_ that returns a syntax would appear to be allowed.

(define ops `(,* ,and))
((car ops) 2 3)
((cadr ops) #t #f)

What about (and and) which is an _expression_ that returns a syntax.

((and and) #t (+ 1 2 3))

The WG1 charter asserts that "Self consistency is an important objective,"
and yet the boolean operators and arithmetic operators do not behave
consistently. In particular as a user of the language it is not obvious
which functions have been implemented as primitives and which as a syntax.

It appears to be valid to implement the "and" function as a primitive or
as a macro and curiously the boolean "not" function is listed with the
Standard Procedures making it inconsistent with the other boolean operators.

There is another situation in which the difference between primitive
procedures and syntax objects makes itself a nuisance and that is in
short-cicuit evaluation of the boolean operators. In the following
_expression_ one might think that the "never" object was not evaluated:

(and #f never)

and yet if the "never" object is a syntax it would seem that it is evaluated.

(and #f and)

If these topics have been discussed previously I apologize and please
refer me to the earlier discussion,

Stephen Lewis

Scheme-reports mailing list

Scheme-reports mailing list