From: Barry Margolin
Subject: Re: CL standardization (identifiers'semantics and symbols)
Date: 
Message-ID: <kqj0ccINN73a@early-bird.think.com>
In article <······················@waxwing.parc.xerox.com> ········@waxwing.parc.xerox.com (Vincent Delacour) writes:
>CL's identifier's semantics are heap-based because changes in the
>package system state are permanent (until the world is restarted ;-),
>so you can never tell for sure what a name occurence, such as foo:bar
>(or simply foo) in a variable position, means --- unless you have a
>record of all package manipulations performed so far.

That's why we advocate performing all those package manipulations using
DEFPACKAGE before loading anything else.  So long as your code contains no
explicit calls to IMPORT, EXPORT, etc., you should not have any problems.
Note that runtime calls to INTERN (either explicitly or from the reader)
are not a problem, because it will either find one of the symbols that was
created by DEFPACKAGE or it will create an internal symbol; it will never
create a symbol that affects inheritance relationships between packages.

>Indeed, in most programming languages, the semantics of programs are
>statically deducible from the their texts. For those who are familiar
>with modular languages, just think of programs written in one of these
>languages, and using a "symbolic computation" module or library
>(offering read, intern, etc) to perform symbolic computations. Just
>see that:

No language with EVAL can have semantics that are statically deducible.
This is what makes Lisp different from these other languages, and the
relationship between symbols and identifiers is a consequence of this.

>The very peculiar semantics of identifiers in CL make directly or
>indirectly most of the language's complexity. They forced the language
>designers to introduce special-purpose constructs (such as eval-when)
>to deal at a source level with the implications on the
>compilation-time load-time and execution time of the entanglement of
>heap-living structures, and identifiers semantics.

Most uses of EVAL-WHEN have nothing to do with packages.  Consider the
macro DEFVAR.  It has to expand into code that will inform the compiler
that the specified variable is special; it does this by including

(eval-when (:compile-toplevel :load-toplevel :execute)
  (proclaim '(special ,variable-name)))

in its expansion.  If the expansion just included an unadorned (proclaim
'(special ,variable-name)), the PROCLAIM function wouldn't be executed
until the code was loaded (since PROCLAIM is just an ordinary function).
(Note that this particular example uses the ANSI semantics of PROCLAIM --
CLtL1 specified that the compiler must recognize top-level calls to
PROCLAIM and interpret them at compile time, which was a kludgey special
case; ANSI CL specifies uniform semantics for top-level function calls, and
only requires the compiler to expand macros and process special forms
specially).

>As pointed out by somebody, " Identifiers are names, symbols are
>objects" (and these are different notions). 

As I mentioned above, that distinction breaks down as soon as you use EVAL,
call COMPILE with two arguments, (coerce '(lambda ...) 'function), or do
anything else that permits you to manipulate symbols in programs as data.
Something as simple as the SYMBOL-VALUE function jumps the name/object gap.

>					     That is precisely the
>reason why they should not be as tight as they are in current Common
>Lisp. Notice that, indeed, programs are entitled to manipulate symbols
>of any kind (symbols are one of the niceties of Lisp aren't they?).
>Programs can read data, create packages, etc. But these are
>manipulations of the global (heap-based) state of the symbols/package
>system. They can affect the semantics of future compilations, load
>operations, etc.

Yes, that's true.  So don't use those troublesome functions if you can't
keep track of their effects.  Just use DEFPACKAGE and treat it just like a
static module definition in any other language.

>Conversely, if by means of package/module constructs you modify the
>package/symbol state, this will be seen by the program itself when it
>reads data (for example, foo::bar will read eq to hux::bar) or
>performs other symbolic computations.

The real problem seems to be that whether you want a local or inherited
symbol is context dependent.  If the data you're reading is symbols that
name functions you would want the equivalence of FOO::CAR and
COMMON-LISP:CAR to be noticed; however, if you're reading symbols that name
types of vehicles you would probably want a distinct FOO::CAR.

>Some seem to advocate that programs that manipulate symbols should not
>perform package operations other than (at most) creating packages and
>interning symbols. The underlying assumption is that complex
>operations on the package's state (such as importations, shadowings,
>etc) belong to the modules world. Indeed, but in current Common Lisp,
>any interned symbol (and interned symbols may be convenient ;-)
>belongs (in some way) to a package. So when you manipulate symbols,
>(for example if you read some data) you are very likely to affect the
>packages/symbols state as well. Unfortunately, even _creating_ a
>symbol may break a program (for ex: it will not compile anymore), and
>may require either some package surgery, or restarting Lisp.

As I said before, if you perform all the operations that affect
inter-package relationships before you start using the packages for
anything else, you are perfectly safe.  Languages such as Ada enforce this
by not permitting runtime manipulation of the package data structures;
indeed, they don't permit runtime manipulation of symbols at all!  Common
Lisp leaves the decision to the programmer: play it safe by declaring
everything ahead of time with DEFPACKAGE, or live dangerously by calling
IMPORT and EXPORT at runtime.

>In conclusion, I can only but repeat that the current semantics of
>symbols and identifiers in Common Lisp are unsafe, and constitute a
>serious drawback of the language; these current semantics
>comes only from previous implementation techniques. They could be
>fixed without affecting the taste of the language, to the great
>benefit of both. 

I still don't believe the "without affecting the taste of the language"
claim.  If packages aren't associated with symbols, what package would the
following look the function binding up in, and how would the user
responding to the prompt specify that a different package be used?

(defun frob-1-and-2 ()
  (format t "~&Please type a function name: ")
  (let ((name (read)))
    (check-type name (and symbol (satisfies fboundp)))
    (funcall (symbol-function name) 1 2)))

Lisp programs pass function, variable, type, class, etc. names around as
data like this all the time.  In my experience, *most* uses of symbols as
data eventually refer to their binding in some namespace.  Common Lisp's
ability to manipulate pieces of programs as data prevents the kind of
static analysis you would like to see.
-- 
Barry Margolin
System Manager, Thinking Machines Corp.

······@think.com          {uunet,harvard}!think!barmar