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

I Like a Single Namespace



Issues in Function and Value Cells
------ -- -------- --- ----- -----

I have been reading "Issues of Separation in Function Cells and Value Cells"
and would like to add some comments. It is interesting to compare the choices
discussed in the paper, and taken in Common Lisp, with similar choices in the
language Pop11 (as instantiated in Poplog Version 11 onwards).

Pop is a good language for comparison purposes, as it falls into the same
general class as Lisp: imperative, interactive (and incremental), dynamically
typed, extensible. (In contrast to Lisp, it is compile-only, has orthodox
syntax, uses an open stack, and does not unite the notions of list and
program. But these are separate issues.)

Pop has (shallow) dynamic binding and (full) lexical binding, with variables
having only one value cell. In practice there does not seem to be any
particular problem with local names shadowing global ones (since Pop11 is
intended as, amongst other things, a teaching language, this is a significant
point). Why is this?

Partly because of the names of the built-in procedures. Some of them, such as
the underlying primitive "sysconslist", have names a user (especially a
beginner!) would be unlikely to choose. Some of them have such common and
well-entrenched meanings that it doesn't occur to anyone that they might use
them as variables (eg, "hd", the approximate equivalent of "car"). Many of
them are spelt with sign characters, eg the list brackets "[" and "]", the
append operator "<>"; it is less natural to use names like this for ordinary
variables,  especially for programmers originally trained in Pascal or C,
where the set of infix operators is not extensible.

The other reason is that those names are "protected"; they cannot be
redeclared or assigned to. Hence one learns rapidly which names are acceptable
and which are not; there are only a few names one misses being able to use
("in" and "to" are the ones that come to mind, and I once made the mistake of
reserving "a"). Experienced users can unprotect - and protect - names if they
so desire.

What about efficiency? Since there is only one value namespace, mustn't every
call be checked to ensure it is a procedure object being applied? In general,
yes.

The first observation is that Pop programs run "fast enough", so clearly the
inefficency induced by checking calls of every user procedure is not that
high. The second is that the only sort of type declaration supported (at
present) by the underlying implementation is that of declaring a variable to
be of type procedure, in which case only procedure values can be stored into
it and calls can be compiled as direct jumps rather than going through
"apply".

Variables can also be declared as "constant", permitting the compiler to avoid
generating additional indirections in the code (this is true for values other
than procedures as well, of course). Many system procedures are "protected
constant procedure"s. (As it happens, they are also not garbage-collected, and
have absolute addresses, but that's a separate issue).

What have we demonstrated? That, by example, the single namespace is workable,
and the possible disadvantages of efficiency and name clashes are countered by
declarations (a device Common Lisp already supports) and name protection (a
natural device that Common Lisp omits).

Another point worthy of note is the way Pop treats variable declarations. When
a variable is declared, it is annotated as being dynamic ("vars") or lexical
("lvars"). The declaration masks any that would be inherited from an outer
scope. Unlike Common Lisp, variables can be declared lexical at the "top
level" of a compilation (interaction) too: in this case they are local to the
current compilation and are inaccessible from outside it. This turns out to be
very convenient, permitting modules to be defined which expose only a few
names to the outside world, while having many names internally, without having
to use the Pop "section" mechanism (roughly equivalent to Lisp packages, but
with a different set of design mistakes).

Pop distinguishes "dynamic" constants (which are dynamic variables with fixed
values) from "lexical" constants; just as with variables, lexical constants
are visible only within a single compilation unit. Although it is possible to
change a dynamic constant (by re-declaring it; of course all existing
references will still get the old constant, so this facility is primarily
useful when re-loading code when a change has been made) a lexical constant
cannot be redefined.

Naturally procedure names may be defined as being in any of the four classes
"vars", "lvars", "constant", or "lconstant"; this is done with the simple
expedient of allowing one of these words to follow the introductory "define"
of the declaration.

A very interesting difference between Lisp's and Pop's treatment of local
declarations of dynamic variables is the way the dyamic variable is rebound
when the binding procedure is entered - it isnt. The variable continues to
have its existing value until an assignment to it is made; the only effect of
the local redefinition is to ensure that its OLD value will be restored on
procedure exit. This turns out to be useful behaviour, eg for locally
incrementing a counter, or locally augmenting a procedure. For example, in a
simple popup menu procedure which paints its picture inside an editor buffer,
I have a declaration rather like

    vars interrupt = restore_the_screen <> interrupt;

"interrupt" is called when an error occurs, and "<>" will compose procedures
as well as append lists (and vectors). This declaration will temporarily
change interrupt so that it will restore the screen before doing whatever it
used to do; note that for this to work it is ESSENTIAL that the old value of
interrupt be available. Another useful definition is

    vars popliblist = my_library :: popliblist;

within a library-searching procedure to augment the library list "popliblist"
with ones own library; again, it is essential that the old value of the
dynamic variable be available. Without this one has to restore to
unwind-protects or double declarations as in

    (let ((old-special-variable special-variable))
        (let ((special-variable expression-involving-old-special-variable))
            bit-that-does-the-work
        )
    )

where the unholy conspiracy between the structure of let-bindings and the
behaviour of dynamic binding leaves a lot to be desired, especially if more
than one special variable is being so modified.

What is the conclusion here? That a sensible and useful meaning can be found
for top-level lexical bindings ("global" bindings in the terms of the paper),
and that a useful alternative definition for local dynamic variable
declarations can be found and is used in an existing language.


A local guru tells me that I can be contacted at

    kers%hplb.csnet@csnet-relay.arpa

which is my address at HPLabs Bristol.

Kers