From: Chris Dollin said:
/I'm/ not confused. Not about this.
You can't assign to a value. You can only assign a value, to
something else, something not a value. It makes no sense to assign
*to* a value. I'm not talking about <jargon>dereferencing</jargon>
(actually indirect-address or index-register storing in more
sensical machine-language jargon) a pointer value. Now if you want
to make the claim that an lvalue in c is really a pointer, I'd be
willing to listen to your argument. But I'm not going to just
assume you mean that unless you say so and give some reasoable
argument that it covers all cases. (Hmm, if this were baseball, the
cliche would be covers all bases. You get it? Agreement of first
letter of word. Nah, that's just a coincidence.)
The /evaluation/ of the expression `x` will yield an lvalue,
which we can usefully think of as "the address of x".
I have no idea what "evaluation" means in c. In Lisp it means
passing a data-structure to the function EVAL and getting back the
'evaluation' of that expression. There's nothing like that in c,
and in Lisp you certainly don't get back the address of the
expression or anything equivalent or even close.
In fact what you said makes no sense. The address of the expression
is the location of the 'x' byte in the source code. That's the only
place the expression 'x' is located where it could be found later,
in the source file. There's a copy of that byte from the source
file momentarily during compilation, but not anywhere you could
post an address where to find it still there later. If it's
declared external, then there's a symbol-table entry for it in the
compiled object file, to be used by the linker/loader. But at run
time there's no access to either the source file or the linker
symbol table.
So let's back off a bit. This so-called 'evaluation' which you
claims happens in c, happens when/where? In the compiler during
compilation? Or at runtime?? Or some other time?
I admire your ability to produce a cogent and informed argument.
I worry about your ability to juggle junk jargon.
Expressions that "denote a place" are those that can be evaluated
for their lvalues. Those that don't, can't.
What does "evaluated" mean in the context of c?
An 'lvalue' is *not* any kind of value, right?
You can't pass an lvalue as an actual argument to a function,
nor return a lvalue as the result from a function,
nor read an lvalue from an input file,
nor write a lvalue to an output file,
nor store an lvalue somewhere,
nor define a struct to have an lvalue as one of its component slots,
nor define an array of lvalues,
right?
Consider something like
fun f( x ) = ... x := x + 1 ...
... f( 1 ) ...
in a language with pass-by-binding (the formal argument is
bound to the (l)value of the actual argument). The body of
`f` can dink around with `x`, even if the actual argument
is a literal, and without affecting /all/ the places where
the value of `1` is required.
That sounds to me more like the dreaded "undefined behaviour".
For example, suppose in some implemetation of that hypothetical
programming language, all literals are pooled. So wherever the
literal value 1 occurs in a program, that piece of code always
references that single memory-location where that 1 is located in
the pool. So when that f(1) call is executed, the code that gets
executed (assuming passing args via registers) is something like:
load ix1,adr1 ;Loads address of pool1, into index register 1
jsr f
f: load reg1,0(ix1) ;Loads value 1 from pool1, into general register 1
add reg1,pool1 ;Adds value 1 from pool1, getting value 2
store reg1,0(ix1) ;Stores value 2 to pool1
ret
adr1: address pool1 ;Address of pool1, which itself is also in the pool
pool1: integer 1 ;Initial value of 1 in the pool, changed by call to f
Now pool1 contains the integer 2, so if f is ever called again
it'll add 2 to its argument.
Your FORTRAN example demonstrates why it can be better for each
(l)evaluation of `1` to yield a /new/ lvalue, not the same one
every time.
You mean you'd actually be willing to write applications in a
programming language where every time you pass a literal to a
function it allocates more memory and copies the literal to the new
memory and tells the function where to find that newly-allocated
memory containing the copy of the literal?? Since the function
might keep a copy of the pointer, and store that pointer other
places, you can never release that memory, because you have no way
to know if somebody else is still referencing that allocated copy
of the literal. Talk about memory leak!!! (Don't you ever complain
that Lisp is slow or wastes memory after what you've just
advocated!!)
Alterately I suppose you could avoid the memory leak by allocating
a static or stack variable for each such constant-copy storage
location. Effectively it'd be as if you had written this instead:
{ int tmp = 1;
f(tmp); }
With that restriction, you can do that already in C, coding all
those call-by-address yourself:
{ int tmp = 1;
f(&tmp); }
And if you don't want to erase those side-effects each time you are
back at this same piece of code, write this instead:
{ static int tmp = 1;
f(&tmp); }
So just write a replacement for all the standard libraries where
all arguments are pointers to the actual argument, and write all
your own functions the same way, and always use & to pass addresses
of actual arguments, and the C compiler will already forbid you
from saying f(&1).
Now back to the original idea of allocating new memory for each
call: If you're talking about a system with automatic garbage
collection like lisp, then of course there's no memory leak. I
actually started work on a lisp-like system that always passed
addresses so that any function could modify the variable or array
element or structure slot or any other variable place that was
passed (and of course you'd be forbidden to pass literals or other
constants directly to a function that modifies its argument,
because the compiler would know they had the 'const' property like
in C++, so they can be passed only to parameters which also have th
'const' property).
Oops. Typo.
No; in fact you don't. You can have a uniform way of /updating/
the store, and different ways of computing the lvalue. Since
the update is "done" by an operation which is roughly
update( store, lvalue, rvalue )
The PDP-10 had something like this called a "byte pointer", which
provided as a unit all of:
- the machine address of the word containing the byte
- the offset of the byte within the word
- the number of bits in the byte
So if your storage units were always smaller than a 36-bit machine
word and never crossed a word boundary, you could specify a
"location" of a byte via a "byte pointer". The instructions LDB
(Load Byte) and DPB (Deposit Byte) did the obvious thing (loading a
byte and shifting into rightmost part of word, or shifting byte out
of rightmost part of word to desired part when storing).
So a single byte pointer could refer to anything from a single bit
(such as in an extremely large bit array where you have to pack the
bits rather than spread them one-per-word) up to a full 36-bit
word. Thus a byte pointer could be the 'lvalue' you speak of.
On virtually any other machine class, you'd need a struct to
represent an lvalue as a single unit of information. I don't see
that ever used in general, due to lower efficiency compared to
bypassing that level of abstraction to generate the specific
machine code needed directly from the parse tree of the source.
Also, I don't understand what that parameter 'store' is in your
call to the update function/macro. Is that an ENUM constant, to
select one of several types of update operation (the other value of
that ENUM perhaps being 'load'), or do you really have a parameter
called 'store' and if so what value do you pass as the 'store'
parameter??
That's a matter of opinion: whether it's a "clarification" to
make assignment depend on that systematic use of macros is ...
a choice.
All programming languages (except some assembly languages) have a
set of compiler rules, such as when the compiler sees anything of
the form
arr[index] = something
it generates code to compute the address of an array element and
the store some value into the memory location pointed-at. Whether
these are called rules or macros makes no difference in concept,
except that Common Lisp allows any old user to extend the set of
such compile-time interpretations of source code to work with new
user-defined structures just as well as it always worked with
language-defined structures such as arrays.
But that's not the point I was making. My point is that any
source-expression pattern that when used on the right side of a
assignment generates code to fetch a value from a slot within some
data structure, can in principle just as well be used on the left
side of a assignment to generate code to store a value in the same
place. The place where you fetch or store a value is *not* itself a
value, it's just a **place** (or slot). The data entity which you
fetch or store, *that* is a value. There's a difference between a
place, where values are located, and a value, which is stored at
some place. To use 'lvalue' to represent a place where a value is
stored, but to use 'rvalue' to represent the actual value, is
assymetric, and IMO midrot jargon. Better to have symmetric jargon.
Analagously there's difference between an address, such as "140
Main Street" (quotes because I'm talking about the address, what
you see on an envelope, not whatever is at that address), and
whatever is at that address, such as a vacant lot or a house or
office building. (Hmm, I checked with Google, and it's a three
bedroom house for sale, at least at that address in one town in CT
anyway.) I would never refer to the address as an lhouse, like
saying "be sure to write the lhouse on the envelope".
I suppose you could think of the so-called lvalue as a city lot, on
which at various times there might be a grassy pasture or a
Victorian house or a demolished house or a newly rebuilt Eichler
house or another demolished house or a block of apartment buildings.
I don't like calling the house a rvalue and the lot it's on an lvalue.
Gee, it's so easy to get sidetracked by Google. In another city
(namespace), the corresponding address has an antique shop, and in
another it's part of a hotel.
Or think of a toilet stall and whoever happens to be using that
stall at any particular time. Is the person an rperson and the
stall an lperson??
Why do you think using C will do that? It's not as though I'm
unfamiliar with Lisp, after all.
Oh. Since most people here aren't familiar with lisp (after all,
why would anybody use c, except for writing operating systems such
as Linux), after they have seen the light of how much better lisp
is), and since you seem to dismiss all lisp jargon, I just assumed
you weren't as familar with lisp as you are with c. Also for the
benefit of the other "you"s, as in "you-all", all the readers of
this thread, not just you singular. So I thought in a c group it
would be polite to express an algorithm in c if it was easy.
You can do something very similar using Pop11's updaters. They
don't use macros to do it: they use functions plus one assignment
rewrite rule.
"assignment rewrite rule" sounds to me like another way of saying
"setf method". How are they in any way different conceptually?
You somehow tell the compiler what code to actually generate with
it sees function(args) on left side of assigment, by some kind of
rewriting it to call a different function that is used for storage,
or maybe the same function but with a different parameter
combination that invokes storage instead of fetch, right? Or did I
miss what "assignment rewrite rule" means? How is "rewrite" any
different from "macro" in general anyway?? A macro is nothing but a
way of rewriting the source code so something newly generated (the
result of the macro-expansion or source-rewrite) will be compiled,
right?
I tried a Google search on that, but couldn't find relevant info
before I got really tired and in need of a nap.