[elaborate discussion of Lisp equivalent of "new" operator]
No, it's got all the relevance in the world.
It does not.
The way symbols work in Lisp makes it possible to generate
never-seen-before symbols.
At run-time. This does not help you much with your problems with
macros, which occur at compile time. The macro must replace
some code
macro invocation (arguments)
other code
with
some code
macro result
other code
subsequent to which the latter is compiled by the compiler. If the
macro result contains a local variable, that code must contain
something of the form "(let (name" etc. etc. (or some other form that
binds a name) and if that same name happens to occur in the "some
code" portion a collision has occurred. What is more, the names that
occur in the "some code" portion cannot in general be computed from
only the macro invocation's arguments. More generally, the list of
names to avoid cannot in general be computed from only the macro
invocation's arguments.
The "(let (name" or similar is the problem, and it cannot in general
be avoided without resort to either global variables or multiple
evaluation. Even if the macro outputs code that, at run-time later,
will create a novel symbol (using the symbol table that exists in
memory at run time to find an unused one), that same code, at compile
time, must refer to this somehow. It must create a local variable that
refers to that symbol, and that can be used elsewhere in the macro's
output to refer to it again at need, and that local variable must have
a name, and that name will then be the potential cause of a collision.
Adding one layer of indirection does not solve the problem, though it
may well impair efficiency.
Also, a symbol's printed representation is not it's entire representation
Not relevant. The printed representation is all that is available to
the compiler at compile-time. You are apparently suggesting that it is
possible for a macro to take code like
(let (foo ... ))
(macro (argument))
and expand it to
(let (foo ... ))
((let (foo ... ))
...)
with the property that somehow the compiler will know to treat the two
foos as separate, and without variable hiding. But how can it?
More significantly, what you are saying would require as a consequence
that hand-replacing a macro invocation with its expansion could result
in different behavior at run-time than having the expansion done by
the preprocessor pass of the compiler. This would be a violation of
the broadest reasonable definition of a macro.
The nutshell is this: while your "gensym" might well be globally
unique, the code output by the macro, and later compiled by the
compiler, must reference it somehow. That reference must involve a
name, if it is to be referenced more than once. And that name cannot
be guaranteed to be unique, even if its referent can.
GENSYM is not the equivalent of the new operator. It doesn't need to
look at the code where a macro is being called from to do its job. All
GENSYM needs to do is examine the Lisp image and grab an unused
reference.
See above. This must occur at run-time. I am concerned with what
occurs at compile time.
This is not a 100% analogy, but it's like generating a
pointer to an empty reference, that can then be filled in.
This indirection is possible in Java and C as well.
In Java, it is as if you had
WeakReference<T> foo = new WeakReference<T>(someTInstance);
Although the T instance now has no name, and the WeakReference created
at runtime is guaranteed not to collide with any local or global
variables, the name "foo" of the reference TO that reference may
collide with or hide other variables with the name "foo" in that and
enclosing scopes. (Ignore for now the effect of WeakReference on
garbage collection.)
In C, it is as if you had
void *foo = malloc(sizeof(void *));
*foo = &my_obj;
Now, *(*foo) is my_obj and the direct pointer to my_obj is stored in
formerly unallocated space; that is a reference to my_obj that cannot
collide with any other reference in the system, barring bugs in
pointer arithmetic making malloc() stop working correctly (an
unfortunate problem sometimes in C, but a preventable one, by
debugging the pointer use that causes the corruption.)
However, the reference TO that reference again has a name, "foo",
which may collide with other names in the same scope.
Most significantly, C has macros, and one could have a C macro
generate code like the above, malloc'ing a pointer to refer to a
temporary. However, the pointer to this reference must itself be
referenced so that it can be DEreferenced and that temporary used. And
that pointer must have a name for the compiler to be able to do its
job. And a list of all potentially-colliding names in the scope could
not in general be computed as any function of the macro's arguments,
even were C macros to be like Lisp ones, able to replace themselves
with arbitrary computable functions of their arguments.
You have asserted that the Lisp macro's ability to become any function
of its arguments grants it extraordinary powers. I have demonstrated
that one particular and important power you have attributed to them
cannot possibly occur as a result of that trait, however.