Re: Is there a better way?



wrf3@xxxxxxxxxxxxxxx (Bob Felts) writes:

From a purist perspective, I agree with you. But I'm not sure I have
the right mental model of what's happening here. If I may descend to a
"vulgar" langauge, in C I can write:

SomeStructureType foo =
{ { ...}, {...}, {...} ...}

I can freely modify the contents of foo, unless I preface the
declaration with 'const'.

Is this equivalent to the defparameter? Or is there an implicit 'const'
in the Lisp expression? It seems seems strange to me that I can't
declare something like this and use it, without first copying it
somewhere else.

Well, partly, the locus of the information you want goes not with
DEFPARAMETER but with QUOTE and/or self-evaluation. It might seem to
go with DEFPARAMETER in the sense that "not being DEFCONSTANT"
matters, but there is nothing special about DEFPARAMETER forms that
wouldn't be true of any other form that is evaluated at runtime.
So trying to draw the lines between your question and the CL world,
"const" in c says you must not modify the value, and using a literal
in CL (what the CL glossary calls a "constant object") says the same
thing (though it's not always enforced--that depends on your
implementation).

Part of the issue is that C doesn't really have the full language
available at compile time, so it's hard to compare the two languages
side-by-side on the rest. Processing done by C at compile time is
done by a separate macro language that doesn't have the full language
available and so the set of issues the language design had to confront
were different and it led to different conclusions.

The problem is partly that Lisp code might be compiled and might be
interpreted, and when it's compiled, it might be done in a separate
image/program/address-space or it might be done in the execution
address space. Consequently, from a design point of view, there is an
identity problem about any object that is even potentially available
at compile time because, being in a different address space even in
that one scenario, means the object might not have the same identity
at runtime. So that's why literals can't be modified--the whole issue
of externalization of the data becomes involved.

For further reading on externalization, see:this section (and its
subsections) of CLHS, which I think outlines some of the complexity:
http://www.lispworks.com/documentation/HyperSpec/Body/03_bd.htm

You might think "well, I don't need them to have the same identity, I
actually want them copied so I can use them to be modified". On this
point, you need to consider that, unlike C, numbers and strings are not
the only possible literals. Literally (pardon the pun) any object can
be a constant. That means any object would have to be possible to copy,
by the compiler, with only tag information [that is, only its
representational type, no information as to intentional type because
Lisp has no such concept] as a cue. For further reading on why that
is problematic, see:
http://www.nhplace.com/kent/PS/EQUAL.html

(In fact, there is the "infinite tower" issue [fortunately really a
finite tower of arbitrary size, so it does bottom out], since any code
that run at compile time in CL had to be compiled somewhere, even in
that same image or in some previous compilation, and some constants
can have lifetimes over several images.)

So in the end, this is what you end up with. The following is my
summary from memory and if anyone notices an error, they should feel
free/encouraged to mention it; summaries like this are no formal
substitute for the spec but sometimes it helps to boil down the spec
for presentation purposes:

In no case in Lisp may you modify a literal structure, whether in a
program or in a value of a variable, whether DEFPARAMETER or any other
definer is used. If you do, the effects are undefined because both
the interpreter and the compiler are permitted to (but not required to)
coalesce equivalent constants and/or to allocate them in read-only
memory, either of which will have bad effects if you later modify things
(e.g., by allowing you to accidentally modify someone else's structure
you didn't realize you shared or by causing a memory error if you try
to do such modification).

Since DEFCONSTANT is permitted to evaluate the expression early and to
use its value as if it's a literal constant (although it's also permitted
to function like DEFPARAMETER if the implementation chooses), modifying
those values is also a no-no but you're responsible for managing that
yourself.

So, in short, if you want to modify data in Lisp, you want to assure
two things:

(a) That you used actual constructors to make the object, not literal
notation.

(b) That you have not used an operator like DEFCONSTANT that says,
in effect, "I declare that the object this creates is permissible
to create at compile time and therefore may be treated as if a
constant".

Note, too, that backquote doesn't count as a constructor. In the
early days of backquote, in pre-Common Lisp dialects, backquote for a
short time did an affirmative copy and could be relied upon to copy.
But that was found to do excess consing, and so we backed off on that.
It now copies only where it can't figure out how not to, so you can't
tell easily which parts of a backquoted structure are constructed and
which not. [Some people figure that `(,a . ,b) is safe because they
can't think of a way a compiler could outsmart them and not use a
constructor, but I think even then it's theoretically possible [though
probably rarely done] for a compiler to use flow analysis to figure
out where a and b are coming from and to turn that into a constant,
while it's not permitted to do the same for (cons a b) which demands
the calling of a constructor at runtime.]

That's probably also a contributing reason for why the compiler
doesn't copy constants and make them usable at runtime, even beyond
the philosophical problems with copying: if you go back to your const
example, if you omit const, the compiler would have to do the copy
just in case there was to be a side-effect. (Some compilers might
do enough flow analysis to tell that sometimes there was no side-effect
and could avoid it, but in some cases the object would escape and the
compiler would have to assume it could be affected, so would have to
copy it. This would lead to gratuitous copying of things that were
never to be used.

It's easier, and ultimately visually clearer, to simply say that if
you want something created at runtime, you should use imperative
operators that create it, so that the compiler's job is easy and so
that you as a reader have an easier job looking at code and saying
"oh, that's going to take time/space" or "oh, that's just going to use
a constant".

Note, btw, that the CL use of the word "constant" is varied. I've
been using it to mean almost always "constant object" above, I think,
but there may have been a lapse or two. The term "literal constant"
is basically a synonym for "constant object" although the CL glossary
defines it as two separate terms, where "literal" gives enough clue to
rule out some of the stray definitions of "constant".

http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_c.htm#constant
http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_l.htm#literal

(In writing the glossary, I tried to codify the current usage of
language as spoken rather than to force the creation of new language
with some specialized spec-only purpose, which is why the terms are
overloaded as they are; that's the way the people who made the
language really talked, and I wanted people to understand how the
community really spoke about things, especially since the variety of
language is reflective of the variety of implementational theories.)

Does this help?
.



Relevant Pages

  • Re: "STL from the Ground Up"
    ... high-level intermediate language than can interoperate with many other ... If your language lacks expressive features then you cannot write code ... memory management in comparison. ... Mostly because type errors mean that the programmer and compiler disagree ...
    (comp.programming)
  • Re: A note on computing thugs and coding bums
    ... It would handle international characters if the execution character ... method I used in "Build Your Own .Net Language and Compiler". ... work areas and counting on Nul is an illusion. ...
    (comp.programming)
  • Re: access(FULLPATH, xxx);
    ... with "trial& error" to just silence the compiler. ... void *foo); ... given that the language in the specification _was_ abiguous and both ... documentation was paramount. ...
    (freebsd-questions)
  • Writing a Roguelike in D, C# and C++
    ... Since I have seen some interest on this newsgroup about which language ... The official compiler is dmd. ... The roguelike I am writing uses variable sized glyphs. ... trigger Hejlberg's garbage collector. ...
    (rec.games.roguelike.development)
  • Re: WaitForSingleObject() will not deadlock
    ... represent an incorrect implementation of the language. ... the *compiler* does not guarantee this. ... but to state it in terms of the execution instead of the formal semantics of the language ... as long as the optimizations do not change the semantics of the language). ...
    (microsoft.public.vc.mfc)