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

flet/labels/macrolet



  It is our opinion that it is reasonable for a local function/macro to be
able to shadow a special form.  If such shadowing is not allowed, it's
basically just another totally arbitrary rule that the programmer has to
remember.  It also makes for some extra work for program-analyzing
programs, either in the analysis of flet/labels/macrolet forms, where it
must check the names to see if they would shadow any of the standard
special-forms, or at form-processing time (where it is currently impossible
to do portably).

  Note that the description of how such programs should process a form
whose car is a symbol (on page 57 of CLtL) is wrong, in that it may not
correctly handle local definitions.  The key point is that such a program
is allowed to special knowledge for 'functions' other than the 24 standard
special-forms.  If it does have such knowledge, it must first examine the
local environment to see if its definition is being shadowed.
Unfortunately, there is no portable way to examine the local environment.

  Actually, as I'm sitting here writing this, my thoughts keep drifting
into a diatribe against the lack of portable mechanisms for dealing with
environments.  If you want to write a portable code walker that accepts an
initial environment argument, either you simply can't do it if
special-forms can be shadowed, or you have to restrict your code walker to
only knowing about the standard special forms, which means it's still
impossible, if the many complaints I've heard from people trying to write
such things is any indication.

  Well, if I'm going to complain about it, I suppose I should propose a
solution, so here goes ...

  It seems to me that the necessary functionality for examining the
function environment is pretty easy to describe.  All you really need is
something like

    (defun local-function-p (symbol environment)
      "This function returns T if the specified symbol names a local
       function or macro in the specified environment, and Nil otherwise.
       Environment should be either the environment argument passed to a
       macroexpander function (obtained with the &environment
       lambda-keyword), or the environment argument to applyhook/evalhook."
       ...)

  I would expect that, for any given implementation, it is completely
trivial to write the code for local-function-p.  Does anyone know of a
counter-example?  With this function available, when your code walker is
processing a form which is a list whose car is a symbol, it does the
following

  ;; special forms can be shadowed
  (if (local-function-p (car form) environment)
      (multiple-value-bind (newform macrop)
          (macroexpand-1 form environment)
        (if macrop
            << recurse on newform >>
            << treate the form as a function call >>
            ))
      << do the stuff described on page 57 of CLtL >>
      )

  ;; special forms cannot be shadowed
  (cond ((member (car form) *the-infamous-24-built-in-special-forms-names*)
         << run special code >>
         )
        ((local-function-p (car form) environment)
         (multiple-value-bind (newform macrop)
             (macroexpand-1 form environment)
           (if macrop
               << recurse on newform >>
               << treate the form as a function call >>
               )))
        (t
         << do the stuff described on page 57 of CLtL >>
         ))

  The functionality needed for augmenting environments is less obvious to
me, since I haven't studied all that many code walkers to know what their
needs are, nor all that many implementations, to know what their quirks
are, as far as manipulation of environments is concerned.  I think
something like the following would be adequate for the walkers and
implementations I have seen, but feel free to blow holes in it.

    (defun augment-function-environment
           (function-list environment &optional macroletp)
      "Return a new environment which is the same as environment except
       that the functions specified in function-list are visible.
       Environment should be an environment argument passed to a
       macroexpander function or applyhook/evalhook.  Function-list is
       something suitable for the second argument to flet/labels/macrolet.
       If the optional third argument is true, they are added to the
       environment as local macro definitions, otherwise as local
       functions."
      ...)

  The reason for returning a new object, rather than possibly destructively
modifying it, is so that the environment can be captured at a particular
point without risking having it get modified during further processing.
The definitions are needed for macrolet so that macroexpanding within the
augmented environment will work.  I don't know if anything clever needs to
be done with the definitions for local functions.  If so, then rather than
having a macroletp flag this will probably need an argument which must be
(member flet labels macrolet).  Again, this seems like it should be trivial
to write for a given implementation, although I feel a lot less certain
about it for augment-function-environment than I do for local-function-p.
I can't offhand think of a reasonable data structure for implementing
environments which would have problems with this, but that doesn't mean
there aren't any.

  As an argument for this being sufficient, I believe that these two
functions are all that is needed to make the PCL code walker really
portable, up to non-standard special-forms in a particular implementation.

  By the way, I have no strong attachment to the names specified above.
They are only intended to be suggestive.

kab

-------