>> From: Taylor R Campbell
>> Date: Tue, 16 Mar 2010 02:23:51 -0400
>>
>> What advantage does a disjoint data type have over writing (foo
>> 'bar: baz 'quux: zot)? [It] strikes me as needless complication to
>> the language.
I agreed with you for very many years, but a few years back I changed
my mind. I'll try to show you why.
>> Using keyword objects rather than (non-keyword) symbols as the
>> arguments to keyword parameters makes sense in Common Lisp only
>> because of its package system.
It certainly arose that way.
The package system started out as a hack
to allow different unrelated Lisp applications to share the same image
without interfering with each other. The problem is that symbols are
heavily overloaded in old lisp systems. In the earliest lisp, a
symbol had an associated `property list' which stored everything you
needed to know about a symbol. This even included the symbol's
`value' property and possibly its `subr' property (function cell).
In this model, a symbol is actually a named set of mappings. The name
itself had no ontological significance (it was stored as the 'pname'
property), it was just a way of getting ahold of the mapping set.
Two different systems would likely make different uses of the
properties of a symbol, so trying to load two different systems into
the same image wouldn't work. The root cause of this problem is very
tricky to understand, and I'll get to it in a moment, but the
`obvious' problem appears to be one of aliasing: both System `A' and
System `B' refer to symbol `foo', but they aren't referring to the
*same thing*. The obvious solution is to give each system a private
symbol. Symbols were interned in the obarray, so a hack that
allowed you to use multiple obarrays and switch between them was
created. The `multiple obarray hack' was the precursor to the
`package system'.
Although the value and function cells of symbols are firmly
entrenched, there has been a shift away from the practice of using
property lists. When a symbol is used only as a key in a table, there
is less need for collision avoidance. On the other hand, there is an
immediate problem that the symbol 'foo' in package A is *not* the
symbol 'foo' in package B, even though they look the same. The
keyword package comes to the rescue. Keywords are good as `symbolic
keys' because they *don't* have values, functions, or properties. You
can use them freely without worrying about collisions.
So keywords arose as a solution to a problem that was an unintended
effect of a solution to an unrelated problem.
-
Earlier I mentioned that the root cause of the problem was tricky to
understand. The cause isn't really one of aliasing unrelated symbols,
the cause is a misunderstanding of naming. (Unfortunately, I haven't
found a good authority on the issues of naming, so I'm going to fly by
the seat of my pants here.)
Abstractly, we want to be able to refer to objects by names. We do
this by establishing a `context' where names are associated with
values. The minimal requirement is that the `name' can be an
arbitrary sort of thing (like the word "foo") and that the `context'
is what provides the mapping. We also desire that the mapping is
relatively static unless told otherwise.
We do this *all the time* in computer science.
The root cause of `symbol collision' is that early lisps were
confused. A `symbol' in these lisps is not a name at all, but a very
complex sort of beast. At a very early phase in interpreting a
program, a symbolic token (like what a user types) is mapped to a
`symbol object' via the obarray. Further interaction with the symbol
is mediated through the `property list'. A picture of this would sort
of look like this:
obarray plist
symbolic token > Symbol ---X> Value
^
|
Symbol
So a symbol *itself* is really a function from another symbol to a
value. This part of the picture:
plist
-X> Value
^
|
Symbol
But that is a terrible idea. What you *want* is the *other* half of
the picture:
obarray
symbolic token > Symbol >---
And the `symbolic token -> Symbol' mapping via the obarray can be
considered an implementation detail of the reader, we just want this
part:
Symbol >
which, when we later supply a context becomes
context
Symbol >-> Value
The keyword package in Common Lisp accomplishes this by essentially
erasing the components of a symbol that are causing the problems and
using the remaining shell as a stand-in for the conceptual symbol.
(This last step is beautiful kludge. The original idea of a symbol
had all these bells and whistles, but if we plug these h