In the land of C++, I'm used to passing arguments by value or
reference. LISP operates by, urm, slightly different rules.
So, in CMULISP:
* *a*
2
* (defun frob(arg) (setf arg (+ 1 arg)))
FROB
* (frob *a*)
3
* *a*
2
Looks like pass-by-value semantics to me. But I _want_ FROB to modify
its argument; i.e., I'd like pass-by-reference semantics. CL has a
kludgey mechanism for effecting this; I can take advantage of the
sideffect of macros, which seem to be predominately an optimization
mechanism, to get pass-by-reference semantics:
* (defmacro frob1 (arg) `(setf ,arg (+ 1 ,arg)))
FROB1
* (frob1 *a*)
3
* *a*
3
Huzzah! Great! Just what I wanted!
Urm, except that I want this in a recursive function, which you can't
really do with macros. Graham's _On Lisp_ talks about mechanisms for
hacking macros to kinda/sorta/vaguely work recursively (pg. 139-142),
but that won't work for my purposes.
The problem I'm trying to resolve is to "correct" an expression tree
that can suffer from a specific kind of artifact. E.g., whereas "(SIN
X)" is just peachy, sometimes I get "((SIN X))", which is decidedly
not peachy. I want to be able to walk through a tree of expressions,
find those artifacts and correct them; i.e., do something like "(setf
tree (car tree))". The problem is, of course, that "tree" has that
damned pass-by-value semantic, so that after the function terminates,
I'm left with the same ole broken tree.
Another bizarre twist are inconsistencies in function argument
semantics. For example, RPLACA, RPLACD, NCONC, and their kin seem to
enjoy pass-by-reference semantics:
* (defun blah (arg) (rplaca arg 'z))
BLAH
* (defvar *b* '(1 2 3))
*B*
* (blah *b*)
(Z 2 3)
* *b*
(Z 2 3)
Ok, I give up. What's so magical about _those_ functions? Why can't
I do the same thing with SETF? Is there some special magic I can do
with SETF to get the same kind of behavior as with RPLACA, et al.? To
take a stab in the dark, is DEFSETF the desired dark incantation?
And, one annoying side-effect about my recent spate of CL programming
is that when I do my regular day job doing plain ole C++, the C++
compiler seems to get particular delight in informing me that
"(getline" is invalid. ;)
Cheers!
Mark
--
Mark Coletti | mailto:mcol...@lychnobite.org | http://www.lychnobite.org/
Does fuzzy logic tickle?
Lisp is not pass by value but it is not exactly like C++ pass by reference
either. There have been a few detailed discussions about that, I won't try
to repeat it and get something subtle wrong. I *think* a useful way to
think of what you are observing is that here you have passed in a "pointer"
(kind of) to the number object that is 2 and you can't change 2 to 3.
But check this:
CL-USER 1 >
(defun frob3 (list)
(setf (second list) 2))
FROB3
CL-USER 2 >
(defvar *a* (list 'a 'b 'c))
*A*
CL-USER 3 > (frob3 *a*)
2
CL-USER 4 > *a*
(A 2 C)
Your own objects will be likewise modified.
> But I _want_ FROB to modify
> its argument; i.e., I'd like pass-by-reference semantics. CL has a
> kludgey mechanism for effecting this; I can take advantage of the
> sideffect of macros, which seem to be predominately an optimization
> mechanism, to get pass-by-reference semantics:
>
>
> * (defmacro frob1 (arg) `(setf ,arg (+ 1 ,arg)))
> FROB1
> * (frob1 *a*)
> 3
> * *a*
> 3
>
> Huzzah! Great! Just what I wanted!
Don't use macros for stuff like that. In your very simple example (I know
it is just too make a point) the lisp way would be:
(setf *a* (frob *a*))
>
> Another bizarre twist are inconsistencies in function argument
> semantics. For example, RPLACA, RPLACD, NCONC, and their kin seem to
> enjoy pass-by-reference semantics:
>
> * (defun blah (arg) (rplaca arg 'z))
> BLAH
> * (defvar *b* '(1 2 3))
> *B*
> * (blah *b*)
> (Z 2 3)
> * *b*
> (Z 2 3)
>
> Ok, I give up. What's so magical about _those_ functions? Why can't
> I do the same thing with SETF? Is there some special magic I can do
> with SETF to get the same kind of behavior as with RPLACA, et al.? To
> take a stab in the dark, is DEFSETF the desired dark incantation?
You can use setf:
(setf (nthcdr n list) new)
(setf (nth n list) new)
(setf (car list) new) ;; or cdr, cadr, all of those
(setf (fifth list) new)
I've never written code with rplaca or replacd
I'm sure you'll get other ideas and explanations, HTH.
--
Coby Beck
(remove #\Space "coby 101 @ bigpond . com")
Mark Coletti wrote:
>
> Looks like pass-by-value semantics to me. But I _want_ FROB to modify
> its argument;
no you don't. :)
> ...sometimes I get "((SIN X))", which is decidedly
> not peachy. I want to ...do something like "(setf
> tree (car tree))". The problem is, of course, that "tree" has that
> damned pass-by-value semantic, so that after the function terminates,
> I'm left with the same ole broken tree.
Just return tree. The function is probably called tree-cleanup or
tree-normalize or whatever. In C++ you can write:
tree-cleanup (*tree);
do-something (*tree);
In Lisp we write: (do-something (tree-normalize tree))
Same number of parens, two less ;s. :)
>
> Another bizarre twist are inconsistencies in function argument
> semantics. For example, RPLACA, RPLACD, NCONC, and their kin seem to
> enjoy pass-by-reference semantics:
>
> * (defun blah (arg) (rplaca arg 'z))
> BLAH
> * (defvar *b* '(1 2 3))
> *B*
> * (blah *b*)
> (Z 2 3)
> * *b*
> (Z 2 3)
>
> Ok, I give up. What's so magical about _those_ functions?
Nothing. Oddly enough you have to ignore the fiction that there is such
a thing as a list in Lisp, there are just conses which are structures
with two pointers.
Given a function tree-normalize receiving ((sin x)) you could:
(defun ntree-normalize (nested) ;; the n in front by convention means I
may whack my input
(setf nested (car nested)))
...but nested is a local variable, so the caller has to capture the
return value. In the *b* example you operated on the car of a cons cell
so the pointer to that cons cell helb by *b* does show the new value
when printed.
gotta run or I would confuse things even more. :)
--
kenny tilton
clinisys, inc
---------------------------------------------------------------
"Harvey has overcome not only time and space but any objections."
Elwood P. Dowd
> The problem I'm trying to resolve is to "correct" an expression tree
> that can suffer from a specific kind of artifact. E.g., whereas "(SIN
> X)" is just peachy, sometimes I get "((SIN X))", which is decidedly
> not peachy.
I see that you got some answers, but one more
won't hurt.
CL has a built in funtion to do this:
[42]> (setf my-tree '(a b (4 7) ((e ((sin x)) (a (a ((sin x))))))))
=> (A B (4 7) ((E ((SIN X)) (A (A ((SIN X)))))))
[43]> (subst '(sin x) '((sin x)) my-tree :test #'equal)
=> (A B (4 7) ((E (SIN X) (A (A (SIN X))))))
You can use nsubst if you don't mind that the tree
can be modified destructively.
--
Eduardo Muñoz
When you use RPLACA, you are passing the container, the list, by value,
but modifying contents. The C++ analogy is roughly,
struct cons { thing* car; cons* cdr; };
union thing { cons x; atom y; };
void rplaca (cons* l, thing* new-car) {
l->car = new-car;
}
For your expression tree manipulation, you need to think about the
analogous processing. If you have something like this:
'(+ y ((sin x)) z)
you need to be removing the artifact viewed from one level higher up in
the tree than you are doing currently, via (setf (third l) (car (third
l))).
The only time this is a problem is if the root of the tree is an
artifact form. To handle this, people typically stuff the tree into a
containing list before passing it to the expression sanitizer which
walks the tree.
Jeff
> I'm a crusty veteran C++ programmer having to wrangle LISP. Though I
> actually sorta enjoy LISP, I've smacked into some crufty LISP
> weirdness that's driving me insane.
>
> In the land of C++, I'm used to passing arguments by value or
> reference. LISP operates by, urm, slightly different rules.
The rules in CL are simple. There is no such thing as
pass-by-reference. There is "pass-by-object-identity".
>
> So, in CMULISP:
>
> * *a*
> 2
> * (defun frob(arg) (setf arg (+ 1 arg)))
> FROB
> * (frob *a*)
> 3
> * *a*
> 2
>
> Looks like pass-by-value semantics to me.
Close enough, but not quite. Note that a perfectly legal call of FROB
is
(from (+ 3 4 5))
what do you suppose to assign to? Note that you cannot even
manipulate pointers in CL as you do in C/C++ (and rightly so).
> But I _want_ FROB to modify
> its argument; i.e., I'd like pass-by-reference semantics. CL has a
> kludgey mechanism for effecting this; I can take advantage of the
> sideffect of macros, which seem to be predominately an optimization
> mechanism, to get pass-by-reference semantics:
>
>
> * (defmacro frob1 (arg) `(setf ,arg (+ 1 ,arg)))
> FROB1
> * (frob1 *a*)
> 3
> * *a*
> 3
>
> Huzzah! Great! Just what I wanted!
Macros are used to extend the language in addition to "efficiency"
purposes, but this is another issue.
All in all you cannot use directly "pass-by-reference" idioms in CL,
and this is a good thing!
> Urm, except that I want this in a recursive function, which you can't
> really do with macros. Graham's _On Lisp_ talks about mechanisms for
> hacking macros to kinda/sorta/vaguely work recursively (pg. 139-142),
> but that won't work for my purposes.
>
> The problem I'm trying to resolve is to "correct" an expression tree
> that can suffer from a specific kind of artifact. E.g., whereas "(SIN
> X)" is just peachy, sometimes I get "((SIN X))", which is decidedly
> not peachy. I want to be able to walk through a tree of expressions,
> find those artifacts and correct them; i.e., do something like "(setf
> tree (car tree))". The problem is, of course, that "tree" has that
> damned pass-by-value semantic, so that after the function terminates,
> I'm left with the same ole broken tree.
A more detailed example would help. However, there is nothing here
that prevents you from solving similar problems without needing
"pass-by-reference".
> Another bizarre twist are inconsistencies in function argument
> semantics. For example, RPLACA, RPLACD, NCONC, and their kin seem to
> enjoy pass-by-reference semantics:
>
> * (defun blah (arg) (rplaca arg 'z))
> BLAH
> * (defvar *b* '(1 2 3))
> *B*
> * (blah *b*)
> (Z 2 3)
> * *b*
> (Z 2 3)
>
> Ok, I give up. What's so magical about _those_ functions? Why can't
> I do the same thing with SETF? Is there some special magic I can do
> with SETF to get the same kind of behavior as with RPLACA, et al.?
The magic is that you are not considering what these functions are
dealing with. (And SETF as well). There is no inconsistency here.
In your example *B* is set to the LIST (1 2 3). ARG in BLAH gets bound
to that object (obtained in a - let's say - pass-by-value style),
i.e. the LIST (1 2 3). Now RPLACA (an historical remain) does what
the followng expression does
(setf (car arg) 'z)
I.e. you are not modifying ARG, but the object bound to it, i.e. the
LIST (1 2 3).
> To take a stab in the dark, is DEFSETF the desired dark incantation?
Only if you need it. You need other things here.
Pass by reference is used in C/C++, Pascal, Ada, etc etc essentially
because you do not have something that CL has. Ada is pretty good at
this by specifying `in', `out', and `in out' parameters to functions.
I may be wrong, but in Ada83, the choice to implement the parameter
passing conventions as pass-by-reference or not, was left to the
implementor.
In C/C++ (et al.) you use pass by reference when you want to "return
more than one thing". Actually changing the setting of a variable is
usually not so useful. In CL you can use multiple values for that
Whereas in C/C++ you'd write
void foo(int x, something* out1, something_else* out2)
{
out1->q = 8;
out2 = (out2*) malloc(sizeof(something_else));
out2->s = 12 * x;
}
in CL you would write
(defun foo (x out1)
(setf (q out1) 8)
(values out1
(make-something-else :s (* 12 x))))
and then you'd do
(multiple-value-bind (out1 out2)
(foo 2 some-var-containing-something)
;; do something with `out1' `out'2)
Does this help?
So, to go back to your example, we need to see more in detail what you
really want to do.
> And, one annoying side-effect about my recent spate of CL programming
> is that when I do my regular day job doing plain ole C++, the C++
> compiler seems to get particular delight in informing me that
> "(getline" is invalid. ;)
You are on the good path :)
Cheers
--
Marco Antoniotti ========================================================
NYU Courant Bioinformatics Group tel. +1 - 212 - 998 3488
719 Broadway 12th Floor fax +1 - 212 - 995 4122
New York, NY 10003, USA http://bioinformatics.cat.nyu.edu
"Hello New York! We'll do what we can!"
Bill Murray in `Ghostbusters'.
> I'm a crusty veteran C++ programmer having to wrangle LISP. Though I
> actually sorta enjoy LISP, I've smacked into some crufty LISP
> weirdness that's driving me insane.
>
> In the land of C++, I'm used to passing arguments by value or
> reference. LISP operates by, urm, slightly different rules.
By identity.
> So, in CMULISP:
>
> * *a*
> 2
> * (defun frob(arg) (setf arg (+ 1 arg)))
> FROB
> * (frob *a*)
> 3
> * *a*
> 2
>
> Looks like pass-by-value semantics to me. But I _want_ FROB to modify
> its argument; i.e., I'd like pass-by-reference semantics. CL has a
> kludgey mechanism for effecting this; I can take advantage of the
> sideffect of macros, which seem to be predominately an optimization
> mechanism, to get pass-by-reference semantics:
Numbers are not side-effectable. Knowing their identity does not allow you
to pass them.
>
> * (defmacro frob1 (arg) `(setf ,arg (+ 1 ,arg)))
> FROB1
> * (frob1 *a*)
> 3
> * *a*
> 3
>
> Huzzah! Great! Just what I wanted!
Macros operate in the lexical environment of the caller, not the callee.
> Urm, except that I want this in a recursive function, which you can't
> really do with macros. Graham's _On Lisp_ talks about mechanisms for
> hacking macros to kinda/sorta/vaguely work recursively (pg. 139-142),
> but that won't work for my purposes.
You can make a arecursive function and a macro that sets it back just as
you did with a non-recursive function (1+) and a macro that sets it back
(frob1).
> The problem I'm trying to resolve is to "correct" an expression tree
> that can suffer from a specific kind of artifact. E.g., whereas "(SIN
> X)" is just peachy, sometimes I get "((SIN X))", which is decidedly
> not peachy. I want to be able to walk through a tree of expressions,
> find those artifacts and correct them; i.e., do something like "(setf
> tree (car tree))". The problem is, of course, that "tree" has that
> damned pass-by-value semantic, so that after the function terminates,
> I'm left with the same ole broken tree.
Y ou just need to
(setq your-root-here (your-recursive-function-here your-root-here))
> Another bizarre twist are inconsistencies in function argument
> semantics. For example, RPLACA, RPLACD, NCONC, and their kin seem to
> enjoy pass-by-reference semantics:
>
> * (defun blah (arg) (rplaca arg 'z))
> BLAH
> * (defvar *b* '(1 2 3))
> *B*
> * (blah *b*)
> (Z 2 3)
> * *b*
> (Z 2 3)
>
> Ok, I give up. What's so magical about _those_ functions?
Because you have first tried to guess the language semantics rather than
read about it and learned it from a source that explains it.
Then you have guessed wrong.
Then you have built up a mystic theory to help you believe you were right
in empirically guessing instead of looking it up.
Then you have further compounded your error by publishing your mystic
theory and the fact that you didn't just look it up.
Building up superstition about how something works is not a good way to
go about learning a language.
> Why can't
> I do the same thing with SETF? Is there some special magic I can do
> with SETF to get the same kind of behavior as with RPLACA, et al.? To
> take a stab in the dark, is DEFSETF the desired dark incantation?
I can't even begin to tell you how to unravel the number of confusions
in this set of sentences because you've dug yourself in so deep to an
initial confusion without stopping to ask for help.
Start over. Learn what the language really does.
What it really does is to evaluate an expression and then pass the identity
of the result as an argument. It can side-effect the argument. It cannot
change the identity of the argument. Note that side-effecting IS changing
the identity of the parts of the object.
> And, one annoying side-effect about my recent spate of CL programming
> is that when I do my regular day job doing plain ole C++, the C++
> compiler seems to get particular delight in informing me that
> "(getline" is invalid. ;)
I'll pass on this.
You could also deal with the root of the tree by having the function
return the new/modified tree. The trick is to use a helper function
("sanitize-car") that recursively sanitizes the car of the given
argument and then setf-es the car with the new tree so obtained.
For the root, we do not do sanitize-car but simply run sanitize itself:
(defun sanitize (x)
(cond ((atom x) x)
((atom (cdr x)) (sanitize (car x)))
(t (maplist #'sanitize-car x)
x)))
(defun sanitize-car (x)
(setf (car x) (sanitize (car x))))
A minor micro-optimization is to avoid the useless memory-roundtrip
if sanitization has not changed the tree:
(defun sanitize-car (x)
(let ((orig (car x)))
(let ((new (sanitize orig)))
(unless (eq orig new) (setf (car x) new)))))
I have tested the above only on trivial examples, so beware.
--
-Matthias
In fact, it seems he wants the tree to be modified destructively.
But that's probably a stupid idea since one doesn't immediately
know what bits the tree is made out of. So wrapping a setf around
[43] would seem to be the best way to go. (Actually, rearranging one's
code so there wasn't necessarily a big honking global hanging around
requiring to to pass it by name would probably be better, and figuring
out where the artifact came from is probably better still.)
When I started learning Lisp I wrote a lot of code that was basically
Fortran or Pascal or whatever:
Name an Object
Put Something in it
Take the stuff in the object out locally, manipulate, put it back in the object
Take the stuff in ..... again, manipulate, put in some other object
And so forth
Display the final result of the manipulations
Then I learned that it was perfectly fine to rely on Lisp to hand my object
around simply by using it as an argument or a return value. Often the
object in question didn't even need a name. That kind of functional
programming style isn't always the best idea, but it can be particularly
useful for someone coming to Lisp from other languages where data
manipulation tends to be much more primitive and "by-hand".
paul
You want wrong. In Common Lisp, we return values and the caller modifies
whatever needs to be modified. Not all languages are variations on C++
such that you can basically continue to work in C++. E.g., Common Lisp
can return multiple values and programmers use this instead of pass-by-
reference because returning more than one value is better than passing in
pointers to and clobbering somebody else's storage space.
However, if you are so dead set on doing this that you cannot be turned
away from misguded application of C++ semantics, here is one way to
accomplish this:
(defun frob (setter)
(funcall setter 42))
(let ((a 12))
(print a)
(frob (lambda (value) (setf a value)))
(print a))
Note how the variable to be modified is captured and that you cannot
screw around with any pointers.
You can make this more convenient with this macro
(defmacro setter (place)
(let ((value (make-symbol "value")))
`(lambda (,value)
(setf ,place ,value))))
This technique may be used to accomplish half the pass-by-name semantics
from Algol, but there is no way to pass in a variable that will appear as
just a variable without undue amounts of macrology.
But I think you should simply learn to write Common Lisp in Common Lisp.
--
In a fight against something, the fight has value, victory has none.
In a fight for something, the fight is a loss, victory merely relief.
70 percent of American adults do not understand the scientific process.
Lisp uses pass-by-value semantics: the callee cannot modify any binding
of name to value in the caller. However, nearly *everything* in Lisp
is passed as an aggregate, so while you cannot change someone else's
bindings, you *can* change the *contents* of the aggregate.
> Looks like pass-by-value semantics to me. But I _want_ FROB to modify
> its argument; i.e., I'd like pass-by-reference semantics.
You've gotten a lot of responses already, but I just want to clarify
something about Lisp variables.
In C/C++, a variable is simply a name for a piece of memory. A data type
is associated with the memory, and therefore with the name. You might
visualize "int a = 1;" as
+---+
a| 1 |
+---+
and reassigning "a" changes the contents of the box.
In Common Lisp, variables do not have these semantics. Instead, we have
symbols (names) which in turn have bindings to data. You might visualize
"(setq a 1)" as
+---+ +---+
| a | ---> | 1 |
+---+ +---+
and rebinding "a" changes where the arrow points to. One big win is that
the arrow may point to a box of any data type whatsoever.
Note that rebinding a will not change the binding of any other variable
(name) in the program.
> CL has a kludgey mechanism for effecting this;
CL has a different view on the association of human-friendly labels with
program data.
> I can take advantage of the sideffect of macros,
Don't use macros for side-effects on the application logic. You will
lose big when you compile your code.
"Erik Naggum" <er...@naggum.net> wrote in message news:32292772...@naggum.net...
> [outline of call-by-reference elided]
For kicks I fleshed out Erik's mechanism. But I agree with him that modifying
the caller's variables is a *bad thing* and that one should ``write Common Lisp
in Common Lisp''. (At least in this mechanism it is obvious to the caller
that a reference is being made!)
(defstruct reference
name
getter
setter)
(defmethod print-object ((object reference) stream)
(print-unreadable-object (object stream :type t)
(format stream "to ~s" (reference-name object))))
(defmacro reference (variable)
"Construct a reference to VARIABLE for call-by-reference semantics."
(check-type variable symbol)
(let ((new-value (gensym "NEW-VALUE-")))
`(MAKE-REFERENCE
:name ',variable
:getter (LAMBDA () ,variable)
:setter (LAMBDA (,new-value) (SETQ ,variable ,new-value)))))
(defun dereference (ref)
(check-type ref reference)
(funcall (reference-getter ref)))
(defun set-reference (ref new-value)
(check-type ref reference)
(funcall (reference-setter ref) new-value))
(defsetf dereference set-reference)
Hm.. (I'll answer my own question) this is probably better stated as:
CL is pass-by-reference, but notice that numbers and characters are
immutable objects, and the fact that pass-by-value and
pass-by-reference is indistinguishable for immutable objects.
Come to think of it, isn't (or number character boolean) the exact
set of immutable values in CL?
In proper lisp terms, I agree that "pass by identiy" is the right
wording, but I'm not sure that says very much to those that doesn't
already understand lisp.
So, from the implementor's point of view, it's precisely the immutable
property that enables the implementation of numbers and characters
(and booleans) as immediate values. The trade-off being the
immutability of bignums, which makes for example (incf <x>) cons a new
bignum whenever the value of <x> gets too big.
--
Frode Vatvedt Fjeld
> Come to think of it, isn't (or number character boolean) the exact
> set of immutable values in CL?
Boolean is (member t nil), a subtype of symbol. You didn't explicitly
include symbols here, and I don't think they should be included here,
as they have mutable elements. However... they CAN be implemented as
objects which are keys into the symbol-value, symbol-function,
symbol-plist, etc. (hash)tables, and contain immutable references to
only their name and package.
--
-> -/ - Rahul Jain - \- <-
-> -\ http://linux.rice.edu/~rahul -=- mailto:rj...@techie.com /- <-
-> -/ "Structure is nothing if it is all you got. Skeletons spook \- <-
-> -\ people if [they] try to walk around on their own. I really /- <-
-> -/ wonder why XML does not." -- Erik Naggum, comp.lang.lisp \- <-
|--|--------|--------------|----|-------------|------|---------|-----|-|
(c)1996-2002, All rights reserved. Disclaimer available upon request.
> Would it be wrong to say that CL is pass-by-reference, with the
> exception of numbers and characters, which are pass-by-value?
>
> Hm.. (I'll answer my own question) this is probably better stated as:
>
> CL is pass-by-reference, but notice that numbers and characters are
> immutable objects, and the fact that pass-by-value and
> pass-by-reference is indistinguishable for immutable objects.
>
> Come to think of it, isn't (or number character boolean) the exact
> set of immutable values in CL?
>
> In proper lisp terms, I agree that "pass by identiy" is the right
> wording, but I'm not sure that says very much to those that doesn't
> already understand lisp.
Lisp is "call by value". Period. ("Pass by identity" is not a widely
accepted term. I have no idea where Kent got it.)
The story is that some (in fact, many) Lisp _values_ consist of (or
contain) one or more _locations_. Mutation affects the store (which
is a mapping from locations to values implemented via your computer's
memory hardware). This means that larger Lisp data structures such as
lists and trees are really collections of values that are glued
together via the current store. When you change the store (aka
"mutate the data structure"), you change the glue but not the values.
Matthias
> Lisp is "call by value". Period. ("Pass by identity" is not a
> widely accepted term. I have no idea where Kent got it.)
Maybe "pass by eqness" is what it is.
> The story is that some (in fact, many) Lisp _values_ consist of (or
> contain) one or more _locations_. [..]
Aren't you just redefining the meaning of pass-by-value here? Is there
any system that isn't pass-by-value under your definition?
> Mutation affects the store (which is a mapping from locations to
> values implemented via your computer's memory hardware). This means
> that larger Lisp data structures such as lists and trees are really
> collections of values that are glued together via the current store.
> When you change the store (aka "mutate the data structure"), you
> change the glue but not the values.
I don't think this is going to clear things up for anyone. Isn't a
collection of values a value in its own right?
--
Frode Vatvedt Fjeld
> Mark Coletti wrote:
>
>
> > Looks like pass-by-value semantics to me. But I _want_ FROB to modify
> > its argument; i.e., I'd like pass-by-reference semantics.
>
>
> You've gotten a lot of responses already, but I just want to clarify
> something about Lisp variables.
>
> In C/C++, a variable is simply a name for a piece of memory. A data
> type is associated with the memory, and therefore with the name. You
> might visualize "int a = 1;" as
>
> +---+
> a| 1 |
> +---+
>
> and reassigning "a" changes the contents of the box.
>
> In Common Lisp, variables do not have these semantics. Instead, we
> have symbols (names) which in turn have bindings to data. You might
> visualize "(setq a 1)" as
>
> +---+ +---+
> | a | ---> | 1 |
> +---+ +---+
>
> and rebinding "a" changes where the arrow points to. One big win is
> that the arrow may point to a box of any data type whatsoever.
>
> Note that rebinding a will not change the binding of any other
> variable (name) in the program.
As an aside, note that while the above picture is correct, it does not
mean that you cannot compile efficiently (a' la` C) references to
variables whose value is not to be "boxed".
> Matthias Blume <matt...@shimizu-blume.com> writes:
>
> > Lisp is "call by value". Period. ("Pass by identity" is not a
> > widely accepted term. I have no idea where Kent got it.)
>
> Maybe "pass by eqness" is what it is.
(Actually, it's passed by "eqlness", not "eqness". Function calls do
not necessarily preserve "eqness". That's how EQL originated in the
first place--numbers and characters are sometimes passed as arguments
in different registers not known to the GC, and in so doing, their
identity under EQ can be lost, but not under EQL.)
In response to Matthias Blume, though:
I made up "pass by identity". Incidentally, that's how all the other
"pass by" terms were gotten originally; someone made them up. Why did
someone make them up? Because a term was needed. What made a term
needed? Because the absence of a term creating an ongoing problem.
I was taught (in class) that Lisp is "pass by value". But carrying on
a stupid tradition is not helpful. The identical term is used in
languages that employ typed variables instead of typed objects, where
if you pass a vector of ten elements, you actually copy all ten
elements from caller-store to callee-store in order to accomplish this
pass (losing identity in the process). That is _not_ what Lisp does
and it's not helpful to use the same term for an unrelated process.
The world finally more or less understands what object identity is. I
think appealing to that notion clarifies things enormously, and I plan
to just keep using the clear term until it gets currency.
Period. <----- (does this get me an extra debate point?)
As an aside:
It's too bad about the co-opting of the term "object-oriented" by the
rest of the world to mean "having encapsulation". We can quibble
about whether people like the way CL has encapsulation and so whether
CL even has it, but the fact is that Maclisp was, in its time,
considered OO and didn't even _have_ a way to define custom objects.
I allege that the original OO meant "using call by identity". Perhaps
it was the failure to correctly and clearly identify this concept of
OO -- the idea of identity as the central issue -- that led to the
rise in competing languages that did not live up to the properties we
ourselves thought such languages should have. I sometimes use
"identity-oriented" to clarify in retrospect what was originally meant
by "object-oriented" before modern marketeers got their hands on the
term.
> Frode Vatvedt Fjeld <fro...@acm.org> writes:
>
> > Come to think of it, isn't (or number character boolean) the exact
> > set of immutable values in CL?
>
> Boolean is (member t nil), a subtype of symbol. You didn't explicitly
> include symbols here, and I don't think they should be included here,
> as they have mutable elements. However... they CAN be implemented as
> objects which are keys into the symbol-value, symbol-function,
> symbol-plist, etc. (hash)tables, and contain immutable references to
> only their name and package.
I agree about symbols that the notion of mutable/immutable is not a term
of art in CL so depending on how you define it, you get different answers.
Symbols _can_ occur in literal quoted data without their value cells being
rendered immutable; yet symbols do have attributes (property, value,
function) that are potential slots, as Rahul suggests here.
Btw, pathnames are also immutable at the CL level. Some implementations
might make them mutable in a non-portable way. (As a matter of personal
opinion, though, I'd argue that's a serious design mistake.)
> Matthias Blume <matt...@shimizu-blume.com> writes:
>
> > Lisp is "call by value". Period. ("Pass by identity" is not a
> > widely accepted term. I have no idea where Kent got it.)
>
> Maybe "pass by eqness" is what it is.
Except that that does not make any sense (beyond the obvious).
> > The story is that some (in fact, many) Lisp _values_ consist of (or
> > contain) one or more _locations_. [..]
>
> Aren't you just redefining the meaning of pass-by-value here? Is there
> any system that isn't pass-by-value under your definition?
No. I am using the usual meaning of call-by-value. And, yes, there
are systems that do not "pass-by-value". Examples are Haskell and
Miranda (call-by-need aka lazy evaluation), Algol (call-by-name),
Pascal and friends (including C++) where you can declare parameters as
call-by-reference, Ada (can declare parameters as call-by-copy-in-out).
Maybe there are more.
> > Mutation affects the store (which is a mapping from locations to
> > values implemented via your computer's memory hardware). This means
> > that larger Lisp data structures such as lists and trees are really
> > collections of values that are glued together via the current store.
> > When you change the store (aka "mutate the data structure"), you
> > change the glue but not the values.
>
> I don't think this is going to clear things up for anyone. Isn't a
> collection of values a value in its own right?
No. Not in Lisp [*] (at least for most types).
Think of C. C is also strictly call-by-value [**], and yet, a callee can
modify a variable or data structure of the caller if the caller passes
a _pointer_ (aka, a "location") to the callee. The pointer itself is
passed by value, though.
Call by reference can always be simulated via pointers. In other
words, the call-by-reference argument gets an implicit address-of (C's
&) wrapped around it at the call site, and the formal parameter gets a
implicit defererence (C's *) at every occurence in the function's body.
[*] In certain other languages, e.g. Standard ML, there are indeed
values that can be seen as collections of other values (i.e., without
an intervening layer of glue via a location->value mapping). In fact,
almost all of ML's values are of this nature, the exception being values
of ref or array type.
[**] The "special" case of C arrays, which seem to have call-by-reference
semantics, also falls under "call-by-value" via the device of having
an array "decay" into a pointer to its first element.
--
-Matthias
All literals are immutable - '(a b c) is an immutable list, "foo" is
an immutable string, the names of symbols are immutable, etc.
Implementations rarely enforce this, but it is undefined behaviour to
mutate these.
> In proper lisp terms, I agree that "pass by identiy" is the right
> wording, but I'm not sure that says very much to those that doesn't
> already understand lisp.
>
Perhaps "lisp passes references by value" would be more useful to
someone newly come to lisp, or at least confusing in a useful way.
> So, from the implementor's point of view, it's precisely the immutable
> property that enables the implementation of numbers and characters
> (and booleans) as immediate values. The trade-off being the
> immutability of bignums, which makes for example (incf <x>) cons a new
> bignum whenever the value of <x> gets too big.
Immutability isn't sufficient, they also need to be internable, so
that all objects of a given value have the same identity. Immutables
which are otherwise indistinguishable might also be able to have
distinct identities.
As a side note, immutables can be mutated, as long as you do it where
no-one can see :), in which case it becomes an optimisation of garbage
collection/allocation - if you generate a bignum, and incf it, and
have no other references to it, the compiler is free to mutate that
bignum, which is the same as GC'ing it and re-allocating it, only
without bothering with those steps (so the consing isn't inevitable if
your compiler is clever enough, and the situation sufficiently
constrained).
Regards,
Brian.
This is actually a good display of the power of CL macros to extend
the language, though the resulting extension is, as we all agree,
misguided (but that's the price you pay for extensibility!).
You might actually want to add the following macros:
(defmacro with-references ((&rest refs) &body body)
"Setup symbol-macros for the given reference variables, so that
referencing (pun intended) in the body will automatically dereference
them."
(flet ((expand-ref (ref)
(check-type ref symbol)
(list ref `(dereference ,ref))))
`(symbol-macrolet ,(mapcar #'expand-ref refs)
,@body)))
Now you can just write:
(defun my-silly-fun (ref-a ref-b val-a val-b)
(with-references (ref-a ref-b)
(incf ref-b (* val-a (+ ref-a val-b)))
(setq ref-a (* ref-b ref-a))))
And while we are at it, I think you can allow all kind of places
instead of only variables in your reference macro, i.e.
(defmacro reference (place)
"Construct a reference to PLACE for call-by-reference semantics."
(let ((new-value (gensym "NEW-VALUE-")))
`(MAKE-REFERENCE
:name ',place
:getter (LAMBDA () ,place)
:setter (LAMBDA (,new-value) (SETF ,place ,new-value)))))
Isn't CL fun?
Regs, Pierre.
--
Pierre R. Mai <pm...@acm.org> http://www.pmsf.de/pmai/
The most likely way for the world to be destroyed, most experts agree,
is by accident. That's where we come in; we're computer professionals.
We cause accidents. -- Nathaniel Borenstein
> Frode Vatvedt Fjeld <fro...@acm.org> writes:
>
> > Matthias Blume <matt...@shimizu-blume.com> writes:
> >
> > > Lisp is "call by value". Period. ("Pass by identity" is not a
> > > widely accepted term. I have no idea where Kent got it.)
> >
> > Maybe "pass by eqness" is what it is.
>
> (Actually, it's passed by "eqlness", not "eqness". Function calls do
> not necessarily preserve "eqness". That's how EQL originated in the
> first place--numbers and characters are sometimes passed as arguments
> in different registers not known to the GC, and in so doing, their
> identity under EQ can be lost, but not under EQL.)
>
> In response to Matthias Blume, though:
>
> I made up "pass by identity". Incidentally, that's how all the other
> "pass by" terms were gotten originally; someone made them up. Why did
> someone make them up? Because a term was needed. What made a term
> needed? Because the absence of a term creating an ongoing problem.
Sorry to put it bluntly, but this is nonsense. Lisp is one of the
canonical examples of a call-by-value language. The whole family of
"call-by-xxx" talks about the relationship between the expression that
is used to denote an actual argument and the kind of access function
being called gets:
call-by-value: formal parameter is local to the callee, it holds
the value resulting from evaluating the actual argument;
beyond that, callee has no access to the actual argument
call-by-name: formal parameter acts like the actual argument
call-by-need: like call-by-name with added memoization
(Formal parameter acts like the actual argument the
first time it is "needed". After that, the value
calculated the first time around is reused.)
call-by-reference:
This requires having a notion of "lvalue" (expression
denoting a location) in the language. Actual argument
must be an lvalue. Formal parameter then acts as
an rvalue that denotes the same location.
call-by-copy-in-out:
This can be build on top of call-by-reference. It
also requires lvalues. Actual argument must be an
lvalue. Value stored at the location described
by the argument gets copied into callee's local variable
(which is denoted by the formal parameter).
At return time, value in the local variable is copied
back into the location denoted by the actual argument.
I do not see how call-by-eqXXXnees fits in here.
> I was taught (in class) that Lisp is "pass by value". But carrying on
> a stupid tradition is not helpful.
Except when it is not stupid.
> The identical term is used in
> languages that employ typed variables instead of typed objects, where
> if you pass a vector of ten elements, you actually copy all ten
> elements from caller-store to callee-store in order to accomplish this
> pass (losing identity in the process).
That, in fact, is not what's usually called "call-by-value". If you
make a copy of something that can be distinguished from the original,
you have a *new* value. Call-by-value means that the callee gets the
*same* value (but nothing beyond that -- in particular not the
location that this value might have been stored at and not the
expression that was used to calculate it).
> That is _not_ what Lisp does
> and it's not helpful to use the same term for an unrelated process.
I agree that this is not helpful. But the blame here clearly lies
with the improper use of the term "call-by-value" in the "deep copy"
scenario that you describe.
By the way, could you name an example of a language that does the
above? I know that Pascal passes arrays by copying them, but AFAIR
there is no way of asking for array identity. OTOH, all this has
absolutely *nothing* to do with types.
> The world finally more or less understands what object identity is. I
> think appealing to that notion clarifies things enormously, and I plan
> to just keep using the clear term until it gets currency.
Rest assured. It never will.
> Period. <----- (does this get me an extra debate point?)
No. Period.
--
-Matthias
^^^^^^
Ooops, typo. Should be: lvalue.
> call-by-copy-in-out:
> This can be build on top of call-by-reference. It
> also requires lvalues. Actual argument must be an
> lvalue. Value stored at the location described
> by the argument gets copied into callee's local variable
> (which is denoted by the formal parameter).
> At return time, value in the local variable is copied
> back into the location denoted by the actual argument.
>
--
-Matthias
> Would it be wrong to say that CL is pass-by-reference, with the
> exception of numbers and characters, which are pass-by-value?
No, because that is trying to define Lisp calling conventions in
terms of other languages. As such, it is full of exceptions and
odd twists.
> Hm.. (I'll answer my own question) this is probably better stated as:
>
> CL is pass-by-reference, but notice that numbers and characters are
> immutable objects, and the fact that pass-by-value and
> pass-by-reference is indistinguishable for immutable objects.
>
> Come to think of it, isn't (or number character boolean) the exact
> set of immutable values in CL?
Again, although this seems less "exceptional", it still is, because it
is borrowing terminology from other languages.
> In proper lisp terms, I agree that "pass by identiy" is the right
> wording, but I'm not sure that says very much to those that doesn't
> already understand lisp.
I have always liked the phrase "by identity", because it does describe
how lisp passes arguments precisely, and since 'identity' and EQ are
related, it properly describes the transformation of a variable before
it is passed to the argument after it is passed (they are EQ). However,
we've seen that even "by identity" has its encumberances when used in a
universal sense, so I've always used the phrase "pass by LispVal".
What is a LispVal? Glad you asked ...
--
Duane Rettig Franz Inc. http://www.franz.com/ (www)
1995 University Ave Suite 275 Berkeley, CA 94704
Phone: (510) 548-3600; FAX: (510) 548-8253 du...@Franz.COM (internet)
> Lisp is "call by value". Period.
Strong words. But incorrect, as "by value" doesn't properly describe
what is going on.
> ("Pass by identity" is not a widely accepted term.
Precisely why Kent uses it.
> I have no idea where Kent got it.)
> The story is that some (in fact, many) Lisp _values_ consist of (or
> contain) one or more _locations_. Mutation affects the store (which
> is a mapping from locations to values implemented via your computer's
> memory hardware). This means that larger Lisp data structures such as
> lists and trees are really collections of values that are glued
> together via the current store. When you change the store (aka
> "mutate the data structure"), you change the glue but not the values.
What's in an object matters not. Passing arguments is identical to
assignment. What you do with the objects you are assigning or passing
over-and-above the actual assignment/passing has nothing to do with
assignment/passing.
CALL-BY-REFERENCE
An argument passing convention where the address of an argument variable
is passed to a function or procedure, as opposed to where the value of
the argument expression is passed. Execution of the function or procedure
may have side-effects on the actual argument as seen by the caller.
FORTRAN passes arguments by reference.
Here is an example in C++.
void yuck (int& arg) {
arg = 69;
}
void foo () {
int x = 42;
int y = x;
yuck (x); // X is now 69, Y is still 42.
}
Common Lisp does not work this way.
A quick google shows that the definition above seems to be prevalent.
> CL is pass-by-reference, but notice that numbers and characters are
> immutable objects, and the fact that pass-by-value and
> pass-by-reference is indistinguishable for immutable objects.
Numbers and characters are immutable in C++ as well. call-by-reference
refers to the mutability of the binding in the caller.
> Is there any system that isn't pass-by-value under your definition?
The commonly known argument passing conventions are call-by-value, call-by-name,
call-by-need, call-by-reference, and call-by-value-return.
In call-by-value, an argument is evaluated *before* it is passed to
the caller. Suppose function foo were defined as follows:
(defun foo (x y)
(if (zerop x)
most-positive-fixnum
y))
In a call-by-value language, I would get an error if I wrote
(foo 0 (/ 1 0))
In a call-by-name language, however, the variable Y in foo would
not be bound to the value of (/ 1 0), but to the expression `(/ 1 0)'.
Since this expression is not used, the division doesn't happen.
Call-by-need is similar, but there is an efficiency hack. If Y
*were* to be evaluated, the value is cached for future reference.
Call-by-value-return is a kludge for simulating call-by-reference
where creating a reference is problematic (like across the network).
The call is made as a call-by-value call and multiple values are
collected upon return. Then the variables passed in are re-assigned
to the appropriate return values.
> Matthias Blume <matt...@shimizu-blume.com> writes:
>
> > Lisp is "call by value". Period.
>
> Strong words. But incorrect, as "by value" doesn't properly describe
> what is going on.
It does describe *precisely* what's going on, so it is correct.
> > ("Pass by identity" is not a widely accepted term.
>
> Precisely why Kent uses it.
I know that Kent has a knack for making it sound as if Lisp is
something oh-so-special when comparing it to other languages. But at
least as far as argument passing is concerned, it is not.
> > I have no idea where Kent got it.)
>
> > The story is that some (in fact, many) Lisp _values_ consist of (or
> > contain) one or more _locations_. Mutation affects the store (which
> > is a mapping from locations to values implemented via your computer's
> > memory hardware). This means that larger Lisp data structures such as
> > lists and trees are really collections of values that are glued
> > together via the current store. When you change the store (aka
> > "mutate the data structure"), you change the glue but not the values.
>
> What's in an object matters not. Passing arguments is identical to
> assignment.
Only in call-by-value argument passing.
> What you do with the objects you are assigning or passing
> over-and-above the actual assignment/passing has nothing to do with
> assignment/passing.
Right. I was just explaining why there is such a thing as "mutating
the data structure that was passed as an argument in such a way that
the caller can observe the mutation" despite having call-by-value
semantics. This, indeed, has nothing to do with the argument passing
itself but with the nature of the data structure.
--
-Matthias
> No, because that is trying to define Lisp calling conventions in
> terms of other languages. As such, it is full of exceptions and
> odd twists.
it is strange that a language as old as lisp is still unable to name
a calling convention that is as widely used, for whatever its twists
and turns may be. has it changed much over the years and during
the commonization?
oz
--
you take a banana, you get a lunar landscape. -- j. van wijk
> Frode Vatvedt Fjeld <fro...@acm.org> writes:
>
> > Matthias Blume <matt...@shimizu-blume.com> writes:
> >
> > > Lisp is "call by value". Period. ("Pass by identity" is not a
> > > widely accepted term. I have no idea where Kent got it.)
> >
> > Maybe "pass by eqness" is what it is.
>
> (Actually, it's passed by "eqlness", not "eqness". Function calls do
> not necessarily preserve "eqness". That's how EQL originated in the
> first place--numbers and characters are sometimes passed as arguments
> in different registers not known to the GC, and in so doing, their
> identity under EQ can be lost, but not under EQL.)
Hmm, this is news to me. I assume that this occurs on a Lisp Machine?
I've never seen such a thing on GP hardware.
> In response to Matthias Blume, though:
>
> I made up "pass by identity". Incidentally, that's how all the other
> "pass by" terms were gotten originally; someone made them up. Why did
> someone make them up? Because a term was needed. What made a term
> needed? Because the absence of a term creating an ongoing problem.
I agree completely. And I've always liked and accepted your use of
"pass by identity", although it does seem that even "identity" is
somewhat encumbered. It becomes especially problematic to me if there
are actually implementations out there which munge the bits in the
tagged word as they are passed, since to me 'identity' implies EQ.
I've also disliked the usage of the term "tagged pointer" to describe
the words which describe Lisp objects, for the same reason (pointer
implies address, which doesn't exist for an immediate object). So I
have always described these tagged words as LispVals, and thus by using
a completely made-up word I avoid the preconception of whether a
LispVal is a pointer or a value (it is both and it is none). And if
one accepts the made-up and unencumbered term LispVal, it becomes
obvious that Lisp is "pass by LispVal".
[I did not invent the term LispVal, either; and I don't know who did.
I first saw it in the C sources for FranzLisp on the Berkeley Software
Distribution; it was a C typedef which was used to describe the union
of all of the possible lisp types, Maclisp style.]
> Kent M Pitman <pit...@world.std.com> writes:
>
> > I made up "pass by identity". Incidentally, that's how all the other
> > "pass by" terms were gotten originally; someone made them up. Why did
> > someone make them up? Because a term was needed. What made a term
> > needed? Because the absence of a term creating an ongoing problem.
>
> Sorry to put it bluntly, but this is nonsense. Lisp is one of the
> canonical examples of a call-by-value language. The whole family of
> "call-by-xxx" talks about the relationship between the expression that
> is used to denote an actual argument and the kind of access function
> being called gets:
>
> call-by-value: formal parameter is local to the callee, it holds
> the value resulting from evaluating the actual argument;
> beyond that, callee has no access to the actual argument
The problem with this definition, as it applies to Lisp, is that it
does not distinguish what is happening. Passing an argument in CL is
just like binding a LET variable. You get the same lexical distinction
as in a function call, and so in fact passing values around is nothing
new; it doesn't actually describe what is occurring. In C, pass-by-value
implies that a copy is made of the value passed, and thus the value at
the caller's side is protected from mutation (as opposed to Fortran, were
its call-by-reference passing allows a subroutine to modify variables and
sometimes even constants in the caller). In lisp, these immediates happen
to be protected as well, but not by virtue of the value-copying that
occurs in the passing, but by virtue of the immutability of the object,
which is entirely the same in assignment and binding as in argument
passing. In effect, _all_ of Lisp is "by value", not just its argument
passing. So in effect, you are trying to describe Lisp's argument passing
by using terminology that is meaningless in Lisp, because it is how all
of Lisp operates.
When arguing about the correct name for something there are only two (often
unresolvable) approaches: argument by authority and argument by consensus.
You aren't appealing to consensus but you have not provided any citations to
support what you say, so it can not yet be convincing.
For myself, I am only recalling what I was taught when I took C and C++ and
that was that C was call-by-value (including the pointers) and C++ provided
a "new and cool" pass-by-reference mechanism. If you passed a struct in C
as opposed to a reference to struct, your callee received a copy and could
not affect the caller's struct. (Given a pointer, you could, by mechanisms
of indirection get at the memory contents.) That has been consistent with
what I recall reading and hearing about it since then and is consistent with
the FAQ's from the C/C++ world on the subject. (This is not supposed to be
an authoritative argument, just one opinion that counters your own with some
anecdotal support for a consensus argument).
> If you
> make a copy of something that can be distinguished from the original,
> you have a *new* value. Call-by-value means that the callee gets the
> *same* value (but nothing beyond that -- in particular not the
> location that this value might have been stored at and not the
> expression that was used to calculate it).
>
> > That is _not_ what Lisp does
> > and it's not helpful to use the same term for an unrelated process.
>
> I agree that this is not helpful. But the blame here clearly lies
> with the improper use of the term "call-by-value" in the "deep copy"
> scenario that you describe.
>
> By the way, could you name an example of a language that does the
> above?
IIRC, in C++ if you have a class with an array data member (indeed anything
that needs some smarts in terms of copying) the programmer must provide a
copy constructor method so that when an instance of that class is passed by
value the compiler will know how to allocate memory and transfer the
contents. Also, IIRC, that is required before you can even use the
assignment operation.
In my C++ texts it always used pass-by-value and pass-by-reference in the
same way Kent and others here seem to be using them.
>
> > The world finally more or less understands what object identity is. I
> > think appealing to that notion clarifies things enormously, and I plan
> > to just keep using the clear term until it gets currency.
>
> Rest assured. It never will.
Never say never.
--
Coby Beck
(remove #\Space "coby 101 @ bigpond . com")
I think I regret posting that first seed of this idea. :)
> Matthias Blume <matt...@shimizu-blume.com> writes:
>
> > Kent M Pitman <pit...@world.std.com> writes:
> >
> > > I made up "pass by identity". Incidentally, that's how all the other
> > > "pass by" terms were gotten originally; someone made them up. Why did
> > > someone make them up? Because a term was needed. What made a term
> > > needed? Because the absence of a term creating an ongoing problem.
> >
> > Sorry to put it bluntly, but this is nonsense. Lisp is one of the
> > canonical examples of a call-by-value language. The whole family of
> > "call-by-xxx" talks about the relationship between the expression that
> > is used to denote an actual argument and the kind of access function
> > being called gets:
> >
> > call-by-value: formal parameter is local to the callee, it holds
> > the value resulting from evaluating the actual argument;
> > beyond that, callee has no access to the actual argument
>
> The problem with this definition, as it applies to Lisp, is that it
> does not distinguish what is happening. Passing an argument in CL is
> just like binding a LET variable. You get the same lexical distinction
> as in a function call, and so in fact passing values around is nothing
> new; it doesn't actually describe what is occurring.
Just because a description is not new does not mean that it does not
describe what's happening. It could simply mean that what's happening
is not new.
> In C, pass-by-value implies that a copy is made of the value passed,
Nonsense!! In C, as in Lisp, no copy is made. A fresh variable is
being initialized with the *same* value that is being passed, just
like in Lisp. What makes it "call-by-value" in both cases (C and
Lisp) is that an assignment to the formal parameter does not result in
an update of the (l)value that was used as the actual argument.
> and thus the value at
> the caller's side is protected from mutation (as opposed to Fortran, were
> its call-by-reference passing allows a subroutine to modify variables and
> sometimes even constants in the caller). In lisp, these immediates happen
> to be protected as well, but not by virtue of the value-copying that
> occurs in the passing, but by virtue of the immutability of the object,
Wrong. It has nothing to do with immutability of the object. Again,
call-by-value is not about whether you can do something to the data
structure represented by a given value but whether you can do
something to the thing denoted by the expression that was the actual
argument to the function call.
In particular, call-by-reference distinguishes itself from
call-by-value in that the callee can modify the location denoted by
the (l)value (i.e., the _location_) denoted by the actual argument.
But that's not the only location that a callee might want to modify,
and if values _contain_ locations (as it is the case in Lisp as well
as all languages that have the concept of a pointer), then the callee
can perform a mutation that is observable by the caller.
> which is entirely the same in assignment and binding as in argument
> passing. In effect, _all_ of Lisp is "by value", not just its argument
> passing. So in effect, you are trying to describe Lisp's argument passing
> by using terminology that is meaningless in Lisp, because it is how all
> of Lisp operates.
Indeed, one might say that all of Lisp is "by value". This does not
make it meaningless to discuss, and that is *precisely* because we
comparing the way Lisp works with the way *other* languages work.
(Remember that the original poster asked how to do something in Lisp
that he could do oh-so-easily in C++. That's what started the whole
discussion.)
--
-Matthias
Yes. Passing pointers is not the same as pass-by-reference. "Pass by
reference" means that a reference to the storage of the value is passed,
but the value in Common Lisp is a pointer or a number/character. Common
Lisp does _not_ pass a pointer to the pointer-or-number/character.
I'd write this a tad differently:
(defmacro reference (&environment env place)
"Construct a reference to PLACE for call-by-reference semantics."
(multiple-value-bind (dummies vals new setter getter)
(get-setf-expansion place env)
(let ((inner-form `(MAKE-REFERENCE
:name ',place
:getter (LAMBDA () ,getter)
:setter (LAMBDA ,new ,setter))))
(if (and (null dummies) (null vals))
inner-form
`(LET* (,@(mapcar #'list dummies vals))
,inner-form)))))
So this works properly:
(defun bar (array-ref)
(incf (dereference array-ref)))
(defun foo ()
(let* ((array (make-array 6 :initial-contents '(0 0 0 0 0 0)))
(index -1)
(ref1 (reference (aref array (incf index))))
(ref2 (reference (aref array (incf index)))))
(bar ref1)
(bar ref1)
(bar ref2)
array))
Look, this is getting annoying. The reference that is intended in the
term pass-by-reference is not the value of the variable (storage cell),
it is the variable (storage cell) itself. This has been the established
meaning of these terms basically forever. You are seriously confused if
you think pointers as such constitute references.
There are three very, very common ways to pass arguments to functions:
1 Pass by value: The value of the variable (storage cell) is extracted by
the caller and passed to the callee. This also means that all arguments
to a function are just values and a function call can just evaluate all
arguments in order and pass the values to the callee. This is precisely
what Common Lisp specifies.
2 Pass by reference: The variable (storage cell) is passed to the callee,
which can read or write to it. This means that you cannot call a
function with the value of another function call without storing it in a
variable. Fortran has _traditionally_ been pass-by-reference. Ada has
out parameters. C++ has references (coincidence? not!).
3 Pass by name: The expression itself is passed to the callee, which will
re-evaluate it with the values of subexpressions captured in the call.
(They actually pass closures much like I and Joe and Pierre offered with
the setter/reference macro.) This is what Algol and Simula can do.
This is (or should have been) CS 101 material. Please just learn it and
avoid confusing others who might have no education at all and are likely
to listen to bogosities. Having to clean up after such confusions is
both annoying and wasteful when you could have avoided the "information
pollution" in the first place.
>> I don't think this is going to clear things up for anyone. Isn't a
>> collection of values a value in its own right?
>
> No. Not in Lisp [*] (at least for most types).
This seems very peculiar to me. What exactly do you consider to be
mere collections of values, rather than proper values?
--
Frode Vatvedt Fjeld
You also say "C is also strictly call-by-value." Which indicates you
believe there is no difference between C and Lisp argument passing. This is
demonstrably not true.
The following C program produces the output below it:
............................................
#include <stdio.h>
struct thing {int x; int y;};
void foo(struct thing arg)
{arg.x = 1;}
int main(int argc, char* argv[])
{
struct thing bar;
bar.x = 0;
bar.y = 0;
printf("\nbar: x = %d y = %d", bar.x, bar.y);
printf("\ncalling foo...");
foo(bar);
printf("\nbar: x = %d y = %d", bar.x, bar.y);
return 0;
}
.............................output
bar: x = 0 y = 0
calling foo...
bar: x = 0 y = 0
The lisp version does this:
CL-USER 10 > (defstruct thing x y)
THING
CL-USER 11 > (defun foo (arg)
(setf (thing-x arg) 1))
FOO
CL-USER 12 > (defun main ()
(let ((bar (make-thing :x 0 :y 0)))
(format t "~&bar: x = ~A y = ~A"
(thing-x bar) (thing-y bar))
(print "calling foo...")
(foo bar)
(format t "~&bar: x = ~A y = ~A"
(thing-x bar) (thing-y bar))))
MAIN
CL-USER 13 > (main)
bar: x = 0 y = 0
"calling foo..."
bar: x = 1 y = 0
NIL
> > > ("Pass by identity" is not a widely accepted term.
> >
> > Precisely why Kent uses it.
>
> I know that Kent has a knack for making it sound as if Lisp is
> something oh-so-special when comparing it to other languages. But at
> least as far as argument passing is concerned, it is not.
I detect a bit of a chip on your shoulder. Not a useful thing in a
technical discussion.
Anyway, there is clearly something missing in your definitions or in the way
you are applying the labels.
Actually I was blaming it on CS 101 for not debunking two
popular misconceptions thoroughly:
- pass-by-value can have no side-effects
- (eq pass-by-reference (pass-by-value pointer))
Come to think of it, they were the ones that told me those
in the first place. I remember arguing with the prof. over
something he had told the class that was just plain wrong,
and he replied that the concepts being introduced were bad
enough without burying first year students with details not
germane to the day's subject.
--------
Geoff
In Lisp, a CONS cell is a value. But the contents of the CAR of the
cell is not part of that value, it is merely referenced by it. If you
do a RPLACA on the original cell, you do not get a different cell --
so is is still the same value. To take account of this in a semantic
model, a CONS cell is represented as consisting of a pair of
_locations_ (and probably a type tag of some sort), one location
holding the CAR and one holding the CDR. Doing a RPLACA, you still
have the same locations, they simply hold other values (or, to put it
more formally, the external mapping called "store" that associates
each location with a value will have been changed).
So your question above asks the wrong thing. We are not talking about
"mere collections of values", we are talking about collections of
locations. In some languages, where certain composite types such as
CONS are always immutable, one can do away with the step of mapping
locations to values via the store and consider the CAR of a CONS part
of the CONS. On the implementation side you probably still allocate
physical memory for such a CONS (at least in most cases), but you do
not have to mention that in the formal semantic model. In a semantic
model for Lisp you do have to mention locations in some way or the
other because you need to be able to model mutation.
To go back to you question. Suppose you give me ("a collection of")
two values x and y. Now I construct (CONS x y). You will certainly
agree with me that the result of this expression is more than "just
the two values". In fact, I can prove this to you by calling (CONS x
y) again -- which results in something that is observably different
from the first result. Thus, the two values in a CONS are not "just a
collection of two values". Instead, the result of CONS is a
collection of two locations, which initially happen to hold the two
values you gave me. If you do a RPLACA or RPLACD on the result of
CONS, you still have the same cons cell (i.e., you did NOT change this
value).
--
-Matthias
> Passing pointers is not the same as pass-by-reference. "Pass by
> reference" means that a reference to the storage of the value is
> passed, but the value in Common Lisp is a pointer or a
> number/character.
Ok, I was making a false assumption about the meaning of
"pass-by-reference", or actually "call-by-reference". I agree lisp is
call-by-value.
So maybe this description could be correct, and informative for
outsiders:
Common Lisp is call-by-value. Every value is (or can be considered
to be) a pointer, however some values (like numbers, characters, and
nil) are immutable.
Or, using Duane Rettig's word:
Common Lisp is call-by-value. Every value is a LispVal, which is a
tagged word, which is ...
The "every value is a pointer" bit seems to be crucial piece of
information, otherwise people apparently expect the callee to receive
a copy of for example a cons cell. This is what C does, because there
every value is _not_ a pointer.
--
Frode Vatvedt Fjeld
> 1 Pass by value: The value of the variable (storage cell) is extracted by
> the caller and passed to the callee. This also means that all arguments
> to a function are just values and a function call can just evaluate all
> arguments in order and pass the values to the callee. This is precisely
> what Common Lisp specifies.
I have this strange sense of deja-vu. Luckily there is Google:
http://groups.google.com/groups?selm=gat-1002021217330001%40192.168.1.50
---
I think it's worth pointing out that even though Lisp and C++ are both
call-by-value the actual behavior you get from Lisp's calling convention
more resembles (but is not identical to) what you get from
call-by-reference in C++. For example, consider:
class C {
public:
int x;
};
void f(C c) { c.x++; } // c is passed "by value" -- sort of
main() {
C c;
c.x=0;
f(c);
cout << c.x;
}
and the Common Lisp equivalent:
(defclass C () (x))
(defun f (c) (incf (slot-value c 'x)))
(defun main ()
(let ( (c (make-instance 'C)) )
(setf (slot-value c 'x) 0)
(f c)
(print (slot-value c 'x))))
The output of the C++ version is '0' while the Lisp version output is
'1'. (On the other hand, if you changed f in the C++ version to be f(C
&c) then it too would output '1'.) The difference arises because:
> Common Lisp offers object identity as one of its basic features as well
> as the ability to store any kind of object in a Lisp "storage cell"
> location, meaning a variable, structure or class slot, array cell, the
> car and cdr of a cons, etc, etc. This naturally means that Lisp must
> pass around some kind of references to objects, as no other mechanism can
> store any kind of value in equal amounts of space.
This is a subtle but really crucial point. In C++ call-by-value really
means something more like "call-by-copy-of-value" whereas in Lisp
call-by-value means call-by-actual-value. There really is nothing in C++
that mimics the true semantics of Lisp's calling convention. It's a
substantial amount of work to reproduce all aspects of Lisp's argument
passing semantics in C++.
E.
> Duane Rettig <du...@franz.com> writes:
>
> > Matthias Blume <matt...@shimizu-blume.com> writes:
> >
> > > ("Pass by identity" is not a widely accepted term.
> >
> > Precisely why Kent uses it.
>
> I know that Kent has a knack for making it sound as if Lisp is
> something oh-so-special when comparing it to other languages. But at
> least as far as argument passing is concerned, it is not.
But Lisp _is_ something oh-so-special! :-)
> > > I have no idea where Kent got it.)
> >
> > > The story is that some (in fact, many) Lisp _values_ consist of (or
> > > contain) one or more _locations_. Mutation affects the store (which
> > > is a mapping from locations to values implemented via your computer's
> > > memory hardware). This means that larger Lisp data structures such as
> > > lists and trees are really collections of values that are glued
> > > together via the current store. When you change the store (aka
> > > "mutate the data structure"), you change the glue but not the values.
> >
> > What's in an object matters not. Passing arguments is identical to
> > assignment.
>
> Only in call-by-value argument passing.
This is not true is C. Define a small struct. Pass it through a
function call, and assign it to a variable in an assignment statement.
Now, do the new variable and the formal parameter contain the same bits?
Not necessarily, because the struct may have been passed by-value, i.e.
actually copied to the stack, as opposed to being passed as a
"value-by-reference", i.e., a pointer to it copied to the stack and
received by the callee.
The problem here, I think, is in the dual meaning of the term "value".
There seem to be two prevalent meanings to the term "by-value"; one
means "passed after evaluation", and one means "copied". Both are
attempts to meet the real goal of a procedure not affecting the
variables owned by its caller (even so, C does not meet this goal).
This dual meaning is why I object to the term "by-value" to describe
Lisp calling style.
Lisp objects are what are passed, and the values of variables tend not
to be owned specifically by functions, but are owned as a whole by the
process (I suspect that's mostly true of all gc'd languages).
> Duane Rettig <du...@franz.com> writes:
>
> > In C, pass-by-value implies that a copy is made of the value passed,
> Nonsense!!
Of course. How can I argue with such authority?
> In C, as in Lisp, no copy is made.
You should check your facts. I agree that in Lisp no copy is made
(except in the actual transfer of the always-one-word LispVal from
caller to callee). But C is allowed to copy small structs, as part
of the pass-by-value strategy. And most other C values are one-word
values, and as such they are copied or transferred, however you
wish to call it.
See my previous answer to you on this thread.
You are confused. The proper C equivalent of passing a CL struct to a
function is to pass a pointer to a C struct. A plain struct variable
in C is is an lvalue -- something for which there is no direct
equivalent in Lisp.
> Anyway, there is clearly something missing in your definitions or in the way
> you are applying the labels.
If you do not like to take my word, go and look at the literature on
PL semantics.
--
-Matthias
It starts off
Because variables, pairs, and vectors may hold values of any type,
Scheme implementations use a uniform representation for values -- a
single type large enough to hold either a complete value or a
pointer to a complete value, along with the necessary typing
information.
The following sections will present a simple typing system, and
then make some refinements to correct its major
weaknesses. However, this is not a description of the system Guile
actually uses. It is only an illustration of the issues Guile's
system must address. ...
This is disingenuous. The lisp program is not equivalent to the
C program. There are these particular differences:
1. The C code allocates a struct on the stack, the Lisp code
allocates it in main memory. So you'd need to call malloc
in the C code.
2. C allows you to pass structs in argument lists. The way
this works is that the struct is `destructured' at the call
site, and `restructured' at the callee.
Lisp provides this for lists via APPLY and &REST args.
(defstruct (thing (:type list)) x y)
(defun foo (&rest bar)
(setf (thing-x bar) 1))
(defun main ()
(let ((bar (make-thing :x 0 :y 0)))
(format t "~&bar: x = ~a y = ~a"
(thing-x bar) (thing-y bar))
(print "calling foo...")
(apply #'foo bar)
(format t "~&bar: x = ~a y = ~a"
(thing-x bar) (thing-y bar))))
> Matthias Blume <matt...@shimizu-blume.com> writes:
>
> > Duane Rettig <du...@franz.com> writes:
> >
> > > In C, pass-by-value implies that a copy is made of the value passed,
>
> > Nonsense!!
>
> Of course. How can I argue with such authority?
Right, I was wondering, too.
> > In C, as in Lisp, no copy is made.
>
> You should check your facts. I agree that in Lisp no copy is made
> (except in the actual transfer of the always-one-word LispVal from
> caller to callee). But C is allowed to copy small structs, as part
> of the pass-by-value strategy. And most other C values are one-word
> values, and as such they are copied or transferred, however you
> wish to call it.
I know my facts. See my answer to Erann's post.
--
-Matthias
If you assing a struct to a new variable in C, you get the same bits in
different locations -- just like in the case of argument passing.
And, as I have already explained (in reply to Erann's post), this is
due to the lvalue/rvalue distinction and not due to parameter passing.
> The problem here, I think, is in the dual meaning of the term "value".
> There seem to be two prevalent meanings to the term "by-value"; one
> means "passed after evaluation",
No, that's merely "strict". "By value" implies "strict", but not vice versa.
--
-Matthias
No, this interpretation is wrong. The difference that you see (and
which you incorrectly attribute to parameter passing) is that C and
C++ lvalues (of which class instances or structs are examples) are
implicitly coerced ("fetched from") in any rvalue context. This
coercion is what makes a copy of the data.
In Lisp, OTOH, there is no concept of lvalues in the first place.
Your example above works them same way if you do no function calls at
all but simply assign the value of c to some other variable. In C(++)
this causes a copy to be made (because c once again appears in rvalue
context) while in Lisp no such thing happens. Therefore, the effect
you see is not tied to parameter passing but is a consequence of a
more general difference between C(++) and Lisp.
> There really is nothing in C++
> that mimics the true semantics of Lisp's calling convention. It's a
> substantial amount of work to reproduce all aspects of Lisp's argument
> passing semantics in C++.
This is not true either. You get full Lisp calling conventions if you
represent all data as pointers (or, perhaps, the occasional small
integer where appropriate). Conversely, you can get (the effect of)
C(++) calling conventions in Lisp if you choose to represent what
would be lvalues as getter/setter pairs (as Erik has demonstrated).
--
-Matthias
I'd say `nearly' every value is a pointer. Small numbers, characters,
etc. are often immediates.
Hmmm...Things that have SETF expansions? (Since we are doing the
apples and oranges cliche)
--------
Geoff
> This is actually a good display of the power of CL macros to extend
> the language, though the resulting extension is, as we all agree,
> misguided (but that's the price you pay for extensibility!).
I don't agree. The OP's *reason* for wanting something like this is
most definately misguided. However, I've been using a "reified
places"[*] package I wrote, and I'm quite happy with it. It makes
things like modifying graphs *much* easier. I can go:
(with-places ((a (car foo))
(b (slot-value bar 'baz))
(c (gethash crash boom)))
(setf (referees obj) (list a b c)))
And then later, if I want to change the object the referees refer to,
but I want to leave obj alone, I can do it easily, only having to know
about the PLACE class. In the above cases, the PLACE objects
generated will be specialized for cons cells, calls to SLOT-VALUE, and
hash tables. But in the case where the MAKE-PLACE macro doesn't know
about the kind of place being reified, it just makes a pair of
closures, like in your code.
[*] I suspect that my "reified places" package is just a reinvention
of locatives, but I've never seen the documentation for them, so I'm
not sure. But I like my name better, anyway :)
--
/|_ .-----------------------.
,' .\ / | No to Imperialist war |
,--' _,' | Wage class war! |
/ / `-----------------------'
( -. |
| ) |
(`-. '--.)
`. )----'
> Erik Naggum <er...@naggum.net> writes:
>
> > Passing pointers is not the same as pass-by-reference. "Pass by
> > reference" means that a reference to the storage of the value is
> > passed, but the value in Common Lisp is a pointer or a
> > number/character.
>
> Ok, I was making a false assumption about the meaning of
> "pass-by-reference", or actually "call-by-reference". I agree lisp is
> call-by-value.
>
> So maybe this description could be correct, and informative for
> outsiders:
>
> Common Lisp is call-by-value. Every value is (or can be considered
> to be) a pointer, however some values (like numbers, characters, and
> nil) are immutable.
I don't like this phraseology, because in order to explain Lisp in C
terminology, it introduces terminology that is foreign to Lisp, except
to the implementation-savvy (e.g. "what's a pointer?")
> Or, using Duane Rettig's word:
>
> Common Lisp is call-by-value. Every value is a LispVal, which is a
> tagged word, which is ...
Um, I can't seem to find any reference to this quote. It is possible
that I said it, but please provide an actual reference to it so that
I can comment on the context. I may have said this in the same
way that I have always said that I like the term "call-by-identity".
And in fact, call-by-value is much more accurate than call-by-reference,
but I believe that I have always tried to qualify such statements with
context. In fact, it is probably accurate to say that Lisp is
call-by-value, according to a partial usage of the accepted terminology,
but that tends to confuse people who think of call-by-value in the
other accepted terminology usage.
> The "every value is a pointer" bit seems to be crucial piece of
> information, otherwise people apparently expect the callee to receive
> a copy of for example a cons cell. This is what C does, because there
> every value is _not_ a pointer.
But every value is _not_ a pointer. That's the problem. This group is
trying to describe a Lisp concept in non-Lisp terminology, and it doesn't
work! Lisp needs (and has always needed) a term that describes its
own style.
For the record, I accept the term call-by-value, because it is more
correct than call-by-reference, but I like better the term
call-by-identity, because it has fewer encumberances on it than
call-by-value, but in fact I have always preferred call-by-LispVal,
which has _no_ encumberances on it in terms of pre-defined or heisted
usages, and I have always then been willing to define my use of the
term LispVal. I suppose that this problem exists for other languages,
and thus the term LispVal would be too Lisp-specific to be used
more universally, so I am always open to even-yet-a-better-term.
> Perhaps "lisp passes references by value" would be more useful to
> someone newly come to lisp, or at least confusing in a useful way.
This is pretty much how I describe Lisp's calling/binding to people
new to it. "If you pretend that EQ doesn't exist, and only use EQL,
variables are all pointers to values, and calling and binding are by
value." And I quickly explain that that's how it *looks* to the user,
but the implementor can do whatever they want, so long as the preserve
the illusion. It's a useful way of communicating the semantics,
whereas if I just said "Lisp is call-by-value", most people would get
entirely the wrong idea.
> "Pierre R. Mai" <pm...@acm.org> wrote in message news:87lmb2r...@orion.bln.pmsf.de...
> >
> > And while we are at it, I think you can allow all kind of places
> > instead of only variables in your reference macro, i.e.
> >
> > (defmacro reference (place)
> > "Construct a reference to PLACE for call-by-reference semantics."
> > (let ((new-value (gensym "NEW-VALUE-")))
> > `(MAKE-REFERENCE
> > :name ',place
> > :getter (LAMBDA () ,place)
> > :setter (LAMBDA (,new-value) (SETF ,place ,new-value)))))
> >
>
> I'd write this a tad differently:
>
> (defmacro reference (&environment env place)
> "Construct a reference to PLACE for call-by-reference semantics."
> (multiple-value-bind (dummies vals new setter getter)
> (get-setf-expansion place env)
> (let ((inner-form `(MAKE-REFERENCE
> :name ',place
> :getter (LAMBDA () ,getter)
> :setter (LAMBDA ,new ,setter))))
> (if (and (null dummies) (null vals))
> inner-form
> `(LET* (,@(mapcar #'list dummies vals))
> ,inner-form)))))
Agreed, those are probably saner semantics for the places cases ;)
Regs, Pierre.
--
Pierre R. Mai <pm...@acm.org> http://www.pmsf.de/pmai/
The most likely way for the world to be destroyed, most experts agree,
is by accident. That's where we come in; we're computer professionals.
We cause accidents. -- Nathaniel Borenstein
> Frode Vatvedt Fjeld <fro...@acm.org> writes:
>
> > Erik Naggum <er...@naggum.net> writes:
> >
> > > Passing pointers is not the same as pass-by-reference. "Pass by
> > > reference" means that a reference to the storage of the value is
> > > passed, but the value in Common Lisp is a pointer or a
> > > number/character.
> >
> > Ok, I was making a false assumption about the meaning of
> > "pass-by-reference", or actually "call-by-reference". I agree lisp is
> > call-by-value.
> >
> > So maybe this description could be correct, and informative for
> > outsiders:
> >
> > Common Lisp is call-by-value. Every value is (or can be considered
> > to be) a pointer, however some values (like numbers, characters, and
> > nil) are immutable.
>
> I don't like this phraseology, because in order to explain Lisp in C
> terminology, it introduces terminology that is foreign to Lisp, except
> to the implementation-savvy (e.g. "what's a pointer?")
What do you expect? If you want to explain something in C
terminology, you have to use C terminology. No?
> For the record, I accept the term call-by-value, because it is more
> correct than call-by-reference, but I like better the term
> call-by-identity, because it has fewer encumberances on it than
> call-by-value,
Fewer "encumberances". Is "meaning" an "encumberance"?
The term "call-by-value" is completely standard and dead on in the
case of Lisp.
--
-Matthias
Duane Rettig <du...@franz.com> writes:
> Um, I can't seem to find any reference to this quote.
Sorry, I didn't intend this to come out as a quote, I only used your
(singluar) word "LispVal", according to how I understood its meaning.
> But every value is _not_ a pointer. That's the problem. This group
> is trying to describe a Lisp concept in non-Lisp terminology, and it
> doesn't work! Lisp needs (and has always needed) a term that
> describes its own style.
But everything can be /considered to be/ a pointer, in the sense that
nothing prevents an implementation from having only pointers and no
immediate values at all. And lisp's behavior can be understood in
these terms. And immediate values like fixnums can be viewed as mere
optimizations, enabled by the immutable property of numbers.
> For the record, I accept the term call-by-value, because it is more
> correct than call-by-reference, but I like better the term
> call-by-identity, because it has fewer encumberances on it than
> call-by-value, but in fact I have always preferred call-by-LispVal,
> which has _no_ encumberances on it in terms of pre-defined or
> heisted usages, and I have always then been willing to define my use
> of the term LispVal. I suppose that this problem exists for other
> languages, and thus the term LispVal would be too Lisp-specific to
> be used more universally, so I am always open to
> even-yet-a-better-term.
Well, if you do accept "call-by-*" to refer to variables' bindings,
and not whether pointers are passed (as Erik pointed out, and which
appears to be the conventional meaning), I don't see how lisp is
special in this respect.
--
Frode Vatvedt Fjeld
> "Coby Beck" <cb...@mercury.bc.ca> wrote in message news:ZKeA8.8528$GG6.6...@news3.calgary.shaw.ca...
> >
> >
> > The following C program produces the output below it:
[ ... ]
> This is disingenuous. The lisp program is not equivalent to the
> C program. There are these particular differences:
>
> 1. The C code allocates a struct on the stack, the Lisp code
> allocates it in main memory. So you'd need to call malloc
> in the C code.
OK, you're on:
% cat test2.c
#include <stdio.h>
struct thing {int x; int y;};
void foo(struct thing arg)
{arg.x = 1;}
int main(int argc, char* argv[])
{
struct thing *bar = (struct thing*)malloc(sizeof(struct thing));
bar->x = 0;
bar->y = 0;
printf("\nbar: x = %d y = %d", bar->x, bar->y);
printf("\ncalling foo...");
foo(*bar);
printf("\nbar: x = %d y = %d\n", bar->x, bar->y);
return 0;
}
% cc -o test2 test2.c
% test2
bar: x = 0 y = 0
calling foo...
bar: x = 0 y = 0
%
I've tried this on at least seven architectures (though not every
one we have), and they gave the same result. The point is that
when you pass a small struct object in it is copied.
> 2. C allows you to pass structs in argument lists. The way
> this works is that the struct is `destructured' at the call
> site, and `restructured' at the callee.
> Lisp provides this for lists via APPLY and &REST args.
>
>
> (defstruct (thing (:type list)) x y)
>
> (defun foo (&rest bar)
> (setf (thing-x bar) 1))
>
> (defun main ()
> (let ((bar (make-thing :x 0 :y 0)))
> (format t "~&bar: x = ~a y = ~a"
> (thing-x bar) (thing-y bar))
> (print "calling foo...")
> (apply #'foo bar)
> (format t "~&bar: x = ~a y = ~a"
> (thing-x bar) (thing-y bar))))
I assume from this example that you disagree with the distinction
between passing of values and modifying mutable objects, discussed
earlier in this thread?
I wouldn't call your example disingenuous, but it certainly isn't
general. A cute trick, though ...
> "Coby Beck" <cb...@mercury.bc.ca> writes:
>
[ ... ]
>
> You are confused.
Well, someone is confused...
> The proper C equivalent of passing a CL struct to a
> function is to pass a pointer to a C struct.
Please provide references for such strongly authoritative claims
of propriety.
> Duane Rettig <du...@franz.com> writes:
> > In C, pass-by-value implies that a copy is made of the value passed,
>
> Nonsense!! In C, as in Lisp, no copy is made. A fresh variable is
> being initialized with the *same* value that is being passed
I find this, well, self-contradictory. How do you initalize the fresh
variable without copying ?
(Pass by magic ? :-)
Would you make the same claim for C++? (I'm resotring to C++ as it
allows a more graphical demonstration than C:
---------------------------------------------
#include <iostream>
using namespace std;
struct blume {
int a;
blume(int b):a(b) {};
blume(blume& b):a(b.a) // Copy constructor
{ cout << "Copying !" << endl; }
};
void foo(blume b) {
cout << "Inside foo.\n";
cout << &b << endl;
}
int main() {
blume a(3);
cout << &a << endl;
cout << "About to call foo.\n";
foo(a);
return 0;
}
----------------------------------------------
0x7ffff574
About to call foo.
Copying !
Inside foo.
0x7ffff570
----------------------------------------------
In what sense do you mean that "as in Lisp" no copy is made ? In
Common Lisp, when passing objects (with the possible excepiton of
numbers and characters), no new instance (copy) is made; in (C and)
C++ there is (as can be seen above).
I would guess that what tempts most people accustomed to C++ (such
as the OP), to try to seek call-by-reference in Lisp, is the fear of
the expense of copying large objects in function calls and value
returns. Your use of "as in Lisp" seems to suggest that their fears
are unfounded even in C++ (or that they are justified in Lisp).
Just trying to understand you . . .
Even if you pass a big struct this way, it will be copied. This is
how the C language defines it. No need to run it on N architectures.
The problem is that this is not congruent in any way to what Lisp
does. To make it congruent, you would have to the pointer that was
obtained from malloc to function foo. (I know, you think I have no
authority to such "claim of propriety". Alas, I guess you have to
live with that.)
I have explained several times now that the effect of copying the
struct is not attributable to argument passing but simply to the fact
that a struct lvalue appears in an rvalue context. C semantics
require an implicit "fetch" (aka "copy") to be performed at such a
point. The same thing happens if you assign a struct to another
struct variable even when no function call is involved. (These two
scenarios are the only ones where a struct expression may legally
appear in rvalue context in C.)
Since a variable holding a struct in Lisp is not an lvalue in the C
sense, one does not see this effect there. (A lisp variable is an
lvalue [*], but what it denotes is a single location which the store
maps (or rather: might map) to the collection of (other) locations
that constitute an actual struct. This contrasts with C where a
variable of struct type directly denotes the set of locations that
make up the struct.)
[*] My earlier claim about not having lvalues in Lisp was somewhat
incorrect. What I should have said is what's now in the parenthetical
remark above.
--
-Matthias
> Matthias Blume <matt...@shimizu-blume.com> writes:
>
> > Duane Rettig <du...@franz.com> writes:
>
> > > In C, pass-by-value implies that a copy is made of the value passed,
> >
> > Nonsense!! In C, as in Lisp, no copy is made. A fresh variable is
> > being initialized with the *same* value that is being passed
>
> I find this, well, self-contradictory. How do you initalize the fresh
> variable without copying ?
We are not talking about the same kind of "copy" here. Of course, you
have to copy those 32 (or 64, or however many) bits that make up the
pointer (or small immediate). What you don't copy is what this thing
is pointing to.
Or, to put it another way: How could Lisp get away without making a
copy according to your argument?
[ same inappropriate example as has been posted any number of times
by now snipped ]
> In what sense do you mean that "as in Lisp" no copy is made ? In
> Common Lisp, when passing objects (with the possible excepiton of
> numbers and characters), no new instance (copy) is made; in (C and)
> C++ there is (as can be seen above).
Read my reply to the OP who posted the same irrelevant (since
besides-the-point) stuff. The effect that you see is due to something
entirely different.
--
-Matthias
> > This is a subtle but really crucial point. In C++ call-by-value really
> > means something more like "call-by-copy-of-value" whereas in Lisp
> > call-by-value means call-by-actual-value.
>
> No, this interpretation is wrong.
That may be. Nonetheless the behavior of two apparently equivalent
snippets of Lisp and C++ code do different things, so saying "Lisp and C
both call-by-value" and leaving it at that is, at best, misleading.
> The difference that you see (and
> which you incorrectly attribute to parameter passing) is that C and
> C++ lvalues (of which class instances or structs are examples) are
> implicitly coerced ("fetched from") in any rvalue context. This
> coercion is what makes a copy of the data.
Izzat so? Where in Stroustrup's book does it say this? For that matter,
where in any C++ text does it say this?
This is not to say that you're wrong; I know you're not. But the point
here is not to be right. The point here is to explain how Lisp works to a
C programmer. And most C programmers don't know that this is how C works
because most C books don't explain it this way.
> Therefore, the effect
> you see is not tied to parameter passing but is a consequence of a
> more general difference between C(++) and Lisp.
That's right, but it's a more general difference that most people are not
aware of, and which is absolutely crucial to this discussion. So simply
saying "Lisp and C are both call-by-value languages" while it may be
technically correct is, without further elaboration, misleading.
> > There really is nothing in C++
> > that mimics the true semantics of Lisp's calling convention. It's a
> > substantial amount of work to reproduce all aspects of Lisp's argument
> > passing semantics in C++.
>
> This is not true either. You get full Lisp calling conventions if you
> represent all data as pointers (or, perhaps, the occasional small
> integer where appropriate).
Yes, but that's not nearly as easy to do as the brevity of your
description implies. I didn't say it was impossible, I just said it was a
substantial amount of work. All you've done is give a brief description
of the work.
E.
and wrote earlier in a similar vein:
> In C, as in Lisp, no copy is made. A fresh variable is
> being initialized with the *same* value that is being
> passed, just like in Lisp."
Now, I'm sorry to need such basic concepts explained, but what do you think
it means to make a copy if it is not to create an identical bit pattern in a
different location?
--
Coby Beck
(remove #\Space "coby 101 @ bigpond . com")
Calling my example disingenuous is a comment on my motivation. As such, I
must tell you you are wrong about that and I don't know why you would make
that presumption. But moving on...
> The lisp program is not equivalent to the
> C program. There are these particular differences:
>
> 1. The C code allocates a struct on the stack, the Lisp code
> allocates it in main memory. So you'd need to call malloc
> in the C code.
>
> 2. C allows you to pass structs in argument lists. The way
> this works is that the struct is `destructured' at the call
> site, and `restructured' at the callee.
While I am always happy to know about things like this for my own
well-roundedness I can tell you that as a user of a language the above is
not relevant *at all* to understanding what will happen when I write
"foo(bar);" in C and when I write "(foo bar)" in lisp. That kind of stuff
is not for users it is for compiler writers (I have great respect for people
who write compilers, but no desire be one).
The fact that such contortions as you went through below in lisp and similar
contortions one would have to do with pointer dereferencing to satisfy
Mathias' objections in C, the fact that these contortions are necessary to
produce equivalent programs is very strong evidence that the languages do
behave differently.
If the text books all agree that C and Lisp are both call-by-value then that
is what they say and it is pointless to dispute. But names are supposed to
disambiguate between concepts, and there are clearly two distinct concepts
here. Too claim that they are the same merely because the have the same
label is foolish.
Language (human now, not computer) is for the people who use it not those
who define it. If 90% of programmers (all the C++ ones) think call-by-value
means the behaviour they observe then frankly it is time to update the
definitions. And it is certainly less confusing to come up with a new term
to describe what happens in lisp than it is to just tell them lisp is
call-by-value just like C is.
--
Coby Beck
(remove #\Space "coby 101 @ bigpond . com")
>>>>>>>>>>>>
> In article <fosn5ap...@blume-pcmh.research.bell-labs.com>, Matthias
> Blume <matt...@shimizu-blume.com> wrote:
>
> > > This is a subtle but really crucial point. In C++ call-by-value really
> > > means something more like "call-by-copy-of-value" whereas in Lisp
> > > call-by-value means call-by-actual-value.
> >
> > No, this interpretation is wrong.
>
> That may be. Nonetheless the behavior of two apparently equivalent
"Apparently" according to whom? They do not look equivalent to me.
They do not even look similar to me. C and Lisp are different
languages. Simply assuming that there is some sort of 1-1
correspondence is not going to work.
The problem that we are having is that different people seem to have
different ideas of where the 1-1 correspondence breaks down.
> This is not to say that you're wrong; I know you're not. But the point
> here is not to be right. The point here is to explain how Lisp works to a
> C programmer.
I think the point is to be right, not to try to distort one's
explanation with falsehoods just to avoid having to correct someone's
earlier misconceptions. It is the same as with lying: Once you start,
it is impossible to stop. One lie leeds to another, and it becomes
increasingly difficult to stay consistent.
> And most C programmers don't know that this is how C works
> because most C books don't explain it this way.
That is unfortunate, but not my fault. :-)
> > > There really is nothing in C++
> > > that mimics the true semantics of Lisp's calling convention. It's a
> > > substantial amount of work to reproduce all aspects of Lisp's argument
> > > passing semantics in C++.
> >
> > This is not true either. You get full Lisp calling conventions if you
> > represent all data as pointers (or, perhaps, the occasional small
> > integer where appropriate).
>
> Yes, but that's not nearly as easy to do as the brevity of your
> description implies. I didn't say it was impossible, I just said it was a
> substantial amount of work. All you've done is give a brief description
> of the work.
Well, I have done this work several times over. Trust me, it is
really not hard at all compared to many other things that come up when
implementing HLLs.
--
-Matthias
Someone else has already spotted this inconsistency in my somewhat
sloppy speech.
The basic problem is that of trying to compare apples and oranges:
There simply is no Lisp equivalent of a C variable of struct type. A
variable holding a struct in Lisp is equivalent to a C pointer to a C
struct. In other words, there is a difference in the nature of what
"struct values" are.
In C, a variable of struct type denotes a collection of the very
locations that make up the struct. In Lisp, a variable that happens
to currently hold a struct value denotes one single location, and that
location happens to be mapped (by the store) to the collection of
other locations that make up the struct.
In C and in Lisp, when using a variable as the actual argument to a
function call, the bits in all the locations denoted by that variable
get copied into a fresh location denoted by the formal parameter of
the function. It just so happens that in Lisp, the number of
locations so copied is always 1 while in C it can be greater than 1.
A "value", btw., is just those "bits" without the location that stores
them. The bits (and nothing else) get transmitted from the caller to
the callee, hence the name "call-by-value". In contrast,
call-by-reference does not transmit the bits. Instead, it transmits
the location(s) denoted by the argument, and the corresponding formal
parameter ends up denoting those same location(s).
--
-Matthias
> > For kicks I fleshed out Erik's mechanism. But I agree with him that modifying
> > the caller's variables is a *bad thing* and that one should ``write Common Lisp
> > in Common Lisp''. (At least in this mechanism it is obvious to the caller
> > that a reference is being made!)
I disagree. I wrote a similar package awhile back and have found it
to be quite useful, sort of a generalized-container/pseudo-locative
library. While I agree that the OP's quest for by-reference parameter
passing is not in the lisp style, this is still a useful tool to have
around.
It's not at all uncommon in graph-manipulating code to patch links,
and if you're not in a recursive state it can be a bit of a pain,
since you have to remember not only where you came from but how you
came from there.
There are a couple of other uses for them, such as specializing the
reference type for various forms of consistency checks, access
control, triggers, etc, without disturbing too badly the class
containing them or the function using them.
> Kent M Pitman <pit...@world.std.com> writes:
>
> > Frode Vatvedt Fjeld <fro...@acm.org> writes:
> >
> > > Matthias Blume <matt...@shimizu-blume.com> writes:
> > >
> > > > Lisp is "call by value". Period. ("Pass by identity" is not a
> > > > widely accepted term. I have no idea where Kent got it.)
> > >
> > > Maybe "pass by eqness" is what it is.
> >
> > (Actually, it's passed by "eqlness", not "eqness". Function calls do
> > not necessarily preserve "eqness". That's how EQL originated in the
> > first place--numbers and characters are sometimes passed as arguments
> > in different registers not known to the GC, and in so doing, their
> > identity under EQ can be lost, but not under EQL.)
>
> Hmm, this is news to me. I assume that this occurs on a Lisp Machine?
> I've never seen such a thing on GP hardware.
No idea how Lisp Machines do it. Never learned their assembly.
This is how PDP10 Maclisp worked. At the time CL was designed, room for
this option was permitted. I don't know if any CL does it.
"Number-declared" functions had a special entry point (the second instruction)
that you could jump to if you had the data already set right.
If you had it set up generically, the first instruction of such a function
would pushj to a piece of code that would move heap-consed arguments to those
registers.
> I've also disliked the usage of the term "tagged pointer" to describe
> the words which describe Lisp objects, for the same reason (pointer
> implies address, which doesn't exist for an immediate object). So I
> have always described these tagged words as LispVals, and thus by using
> a completely made-up word I avoid the preconception of whether a
> LispVal is a pointer or a value (it is both and it is none). And if
> one accepts the made-up and unencumbered term LispVal, it becomes
> obvious that Lisp is "pass by LispVal".
Not the sort of term you'd expect any other language to adopt. ;)
I'd accept "by object"...? LispVal is a foreign term to anyone who uses
the language. It also suggests that it can't be used for any other language,
when in fact I think it should be usable by other languages.
> I have explained several times now that the effect of copying the
> struct is not attributable to argument passing but simply to the fact
> that a struct lvalue appears in an rvalue context. C semantics
> require an implicit "fetch" (aka "copy") to be performed at such a
> point. The same thing happens if you assign a struct to another
> struct variable even when no function call is involved. (These two
> scenarios are the only ones where a struct expression may legally
> appear in rvalue context in C.)
>
> Since a variable holding a struct in Lisp is not an lvalue in the C
> sense, one does not see this effect there. (A lisp variable is an
> lvalue [*], but what it denotes is a single location which the store
> maps (or rather: might map) to the collection of (other) locations
> that constitute an actual struct. This contrasts with C where a
> variable of struct type directly denotes the set of locations that
> make up the struct.)
I hate to blaspheme here in the presence of so much useless formalism,
but all that really matters to me is to be able to explain the
behavior to users in terms they are familiar with. Formalisms exist
to serve me, not me to serve a formalism. When you start talking
about what happens inside and how it's hidden by the surface language,
you're basically giving me all the confirmation I need that I've made
a good decision to avoid this terminology. You're welcome to try to
sway others with the kind of argument you're making above as a kind of
abstract esoteric discussion among language designers; but when it
comes to teaching people and helping them avoid errors, I don't think
any of this tommyrot is how I want to do my teaching. There's way too
much purposeless baggage in your exposition; I don't want my students
thinking about problems in the way you suggest. That doesn't mean
it's not possible to think about it that way--it just isn't my cup of
tea. Thanks for the suggestions anyway, though.
> * "Pierre R. Mai" <pm...@acm.org>
> | Isn't CL fun?
>
> I think I regret posting that first seed of this idea. :)
You should really have known better, knowing this groups
predisposition to expanding on silly ideas at the slightest
provocation. CL is just too much fun...
I apologize for giving the impression that I thought you were being
deliberately misleading. Perhaps I should have said ``That example is
fallacious.''
It illustrates an important difference between C and Lisp, however
it does not illustrate the difference between call-by-value and
call-by-reference.
Since neither Lisp nor C are call-by-reference, it is not possible to
illustrate the difference between call-by-referenc and call-by-value
by comparing them. However you can illustrate call-by-reference in C++:
void bar (int& x) {
x = 69;
}
void foo () {
int x = 42;
int y = x;
bar (x);
}
Bar modifies the binding of X in the caller's stack frame. This is
simply not possible in Lisp.
Another illustration of call-by-value is the common error when using
DELETE:
(let ((x (compute-some-list)))
(delete-if #'evenp x) ;; wrong!
....
)
The issue is that DELETE cannot modify the value of X because it is
passed a copy of X, not a reference to X. (The fact that the value
of X is likely to be a pointer to an aggregate is irrelevant. You
are passing the pointer itself, not a pointer to a pointer.)
>
> > The lisp program is not equivalent to the
> > C program. There are these particular differences:
> >
> > 1. The C code allocates a struct on the stack, the Lisp code
> > allocates it in main memory. So you'd need to call malloc
> > in the C code.
> >
> > 2. C allows you to pass structs in argument lists. The way
> > this works is that the struct is `destructured' at the call
> > site, and `restructured' at the callee.
>
> While I am always happy to know about things like this for my own
> well-roundedness I can tell you that as a user of a language the above is
> not relevant *at all* to understanding what will happen when I write
> "foo(bar);" in C and when I write "(foo bar)" in lisp. That kind of stuff
> is not for users it is for compiler writers (I have great respect for people
> who write compilers, but no desire be one).
I have to disagree with you on this. If you were unaware of where C
allocates structs by default, you might be surprised to find that you
cannot return a pointer to an automatic variable of type struct.
> The fact that such contortions as you went through below in lisp and similar
> contortions one would have to do with pointer dereferencing to satisfy
> Mathias' objections in C, the fact that these contortions are necessary to
> produce equivalent programs is very strong evidence that the languages do
> behave differently.
Well I *hope* that no one thinks that I'm arguing that C and Lisp are
similar languages just because they are both call-by-value!
> If the text books all agree that C and Lisp are both call-by-value then that
> is what they say and it is pointless to dispute. But names are supposed to
> disambiguate between concepts, and there are clearly two distinct concepts
> here. Too claim that they are the same merely because the have the same
> label is foolish.
The concept being distinguished is simply this: do you pass the *contents*
of a variable (whether it is a pointer or not) or do you pass the *address*
of the variable (whether it is a pointer or not). Both C and Lisp do the
former. C++ allows one to do the latter. Fortran does the latter by default.
> Language (human now, not computer) is for the people who use it not those
> who define it. If 90% of programmers (all the C++ ones) think call-by-value
> means the behaviour they observe then frankly it is time to update the
> definitions. And it is certainly less confusing to come up with a new term
> to describe what happens in lisp than it is to just tell them lisp is
> call-by-value just like C is.
C++ programmers who are told that Lisp is call-by-reference would be
equally confused by the behavior of DELETE.
I think that rather than discussing `call-by-value' vs. `call-by-reference',
that the clearest description of Lisp for a C++ programmer is this:
Nearly everything in Lisp is a pointer to a struct.
> g...@jpl.nasa.gov (Erann Gat) writes:
>
> > In article <fosn5ap...@blume-pcmh.research.bell-labs.com>, Matthias
> > Blume <matt...@shimizu-blume.com> wrote:
> >
> > > > This is a subtle but really crucial point. In C++ call-by-value really
> > > > means something more like "call-by-copy-of-value" whereas in Lisp
> > > > call-by-value means call-by-actual-value.
> > >
> > > No, this interpretation is wrong.
> >
> > That may be. Nonetheless the behavior of two apparently equivalent
>
> "Apparently" according to whom?
Me.
> They do not look equivalent to me.
But you are not the target audience for my explanation. The target
audience for my explanation was a C++ programmer who is just beginning to
learn Lisp who has just been told (only) that both C++ and Lisp use
call-by-value semantics.
> They do not even look similar to me. C and Lisp are different
> languages. Simply assuming that there is some sort of 1-1
> correspondence is not going to work.
Obviously. People do nonetheless assume this, and if you're going to
explain things to them effectively you have to take that into account.
> The problem that we are having is that different people seem to have
> different ideas of where the 1-1 correspondence breaks down.
That's right. But if you're going to explain something to someone you
have to do it on *their* terms (assuming, of course, that your goal is
actually to educate someone, and not simply to put your superior knowledge
on display).
> > This is not to say that you're wrong; I know you're not. But the point
> > here is not to be right. The point here is to explain how Lisp works to a
> > C programmer.
>
> I think the point is to be right, not to try to distort one's
> explanation with falsehoods just to avoid having to correct someone's
> earlier misconceptions. It is the same as with lying: Once you start,
> it is impossible to stop. One lie leeds to another, and it becomes
> increasingly difficult to stay consistent.
An approximation to the truth is not the same thing as a lie.
> > And most C programmers don't know that this is how C works
> > because most C books don't explain it this way.
>
> That is unfortunate, but not my fault. :-)
It most certainly is your fault. Where are your publications written for
a non-academic audience? Where is your book on C that explains how
lvalues get coerced in rvalue contexts? Oh, you haven't written any?
Then it is your fault that people don't know this stuff. Your fault, and
mine, and the fault of everyone who understands what's really going on but
hasn't bothered to put in the effort to explain it to people in ways that
they can understand it.
> > Yes, but that's not nearly as easy to do as the brevity of your
> > description implies. I didn't say it was impossible, I just said it was a
> > substantial amount of work. All you've done is give a brief description
> > of the work.
>
> Well, I have done this work several times over. Trust me, it is
> really not hard at all compared to many other things that come up when
> implementing HLLs.
I believe that. But just because it isn't hard relative to something else
doesn't mean it's not "a substantial amount of work."
E.
> and the fault of everyone who understands what's really going
> on but hasn't bothered to put in the effort to explain it to
> people in ways that they can understand it.
to recast fault-finding constructively and paraphrase some old dudes:
what is a good person but a bad person's teacher?
what is a bad person but a good person's job?
thi
I think we need a fresh start.
Argument passing in Common Lisp is conceptually equivalent to building
a list of the result of evaluating each of the argument forms to the
function and passing that list to the function, which conceptually
unpacks it into the formal parameters. All knowledge of the source of
the values is lost by that time.
The conceptual equivalence to a list is used to lay the ground for apply,
&rest, &key, etc, and also neatly captures the order of evaluation so an
explanation of this rule will fall out naturally from the description.
--
In a fight against something, the fight has value, victory has none.
In a fight for something, the fight is a loss, victory merely relief.
70 percent of American adults do not understand the scientific process.
> I hate to blaspheme here in the presence of so much useless formalism,
> but all that really matters to me is to be able to explain the
> behavior to users in terms they are familiar with.
Well, the question is: _Are_ you (able to explain)? I think the
answer is "no". Otherwise there wouldn't be so much confusion about
these issues around here.
> Formalisms exist
> to serve me, not me to serve a formalism. When you start talking
> about what happens inside and how it's hidden by the surface language,
> you're basically giving me all the confirmation I need that I've made
> a good decision to avoid this terminology.
Where did I say anything about something being "hidden" by the surface
language?
> You're welcome to try to
> sway others with the kind of argument you're making above as a kind of
> abstract esoteric discussion among language designers; but when it
> comes to teaching people and helping them avoid errors, I don't think
> any of this tommyrot is how I want to do my teaching.
You are, of course, welcome to do you teaching any way you like.
Whether it actually does your students a favor is debateable, though.
> There's way too
> much purposeless baggage in your exposition; I don't want my students
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Huh? You haven't even made an _attempt_ at trying to understand, have
you? If you had, you would know that there is a purpose to all that.
> thinking about problems in the way you suggest. That doesn't mean
> it's not possible to think about it that way--it just isn't my cup of
> tea. Thanks for the suggestions anyway, though.
You are welcome to use non-standard and ill-defined terms such as
"pass-by-identity". You probably have a good mental model of what you
mean by those, but if you actually try to make them precise, you
inevitably have to do something along the lines of how PL semanticists
usually do things (i.e, the way I tried -- clearly unsuccessfully --
to described it to you).
Most of the trouble with guys like the original poster is that they
tend to have a good graps of neither the semantics of their "home
language" (C++ in this case) nor the new language in question (Lisp).
If people wouldn't dismiss precise semantics out of hand, refusing to
learn them, and if they would not be encouraged in this attitude by
renowned seniority such as yourself, we wouldn't have quite as many of
these discussions. As Erik said, all this is CS101 stuff.
Matthias
> > They do not even look similar to me. C and Lisp are different
> > languages. Simply assuming that there is some sort of 1-1
> > correspondence is not going to work.
>
> Obviously. People do nonetheless assume this, and if you're going to
> explain things to them effectively you have to take that into account.
As much as it is important to build on existing knowledge when trying
to communicate new knowledge, it is important not to further existing
misconceptions (guised as "existing knowledge") by trying to build on
them.
> > The problem that we are having is that different people seem to have
> > different ideas of where the 1-1 correspondence breaks down.
>
> That's right. But if you're going to explain something to someone you
> have to do it on *their* terms (assuming, of course, that your goal is
> actually to educate someone, and not simply to put your superior knowledge
> on display).
Not if "*their* terms" are wrong to begin with. In that case, the
first thing to do is establish new, correct terms.
> > > And most C programmers don't know that this is how C works
> > > because most C books don't explain it this way.
> >
> > That is unfortunate, but not my fault. :-)
>
> It most certainly is your fault. Where are your publications written for
> a non-academic audience? Where is your book on C that explains how
> lvalues get coerced in rvalue contexts? Oh, you haven't written any?
> Then it is your fault that people don't know this stuff. Your fault, and
> mine, and the fault of everyone who understands what's really going on but
> hasn't bothered to put in the effort to explain it to people in ways that
> they can understand it.
I would be ready to take (some of) the blame if the situation were
such that there are *no* C books that explain things correctly. But
that is not so.
C books that explain things correctly exist. Me writing one more
would not have made a difference. Or are you trying to say that I
should write a couple of hundred "correct" C books to give "correct" C
books the majority? :-)
> > > Yes, but that's not nearly as easy to do as the brevity of your
> > > description implies. I didn't say it was impossible, I just said it was a
> > > substantial amount of work. All you've done is give a brief description
> > > of the work.
> >
> > Well, I have done this work several times over. Trust me, it is
> > really not hard at all compared to many other things that come up when
> > implementing HLLs.
>
> I believe that. But just because it isn't hard relative to something else
> doesn't mean it's not "a substantial amount of work."
When I wrote the three lines above, I somewhat expected you to come
back with this reply. Unfortunately, I resisted my urge to say "It is
dead easy." -- which is what I originally intended to write. And yes,
it *is* dead easy. In fact, I don't see at all why you think that
there would be any problem whatsoever.
Matthias
Erik Naggum wrote:
> * "Pierre R. Mai" <pm...@acm.org>
> | Isn't CL fun?
>
> I think I regret posting that first seed of this idea. :)
> I think that rather than discussing `call-by-value' vs.
`call-by-reference',
> that the clearest description of Lisp for a C++ programmer is this:
>
> Nearly everything in Lisp is a pointer to a struct.
I have always thought in terms of, *everything* in Lisp is a pointer
to a struct, but some of those structs are immutable and an attempt
to modify simply creates a new object.
That is, you can think of an integer as being implemented as a struct,
and something like (setf (ldb (byte 8 8) i) 25) simply creates a new
struct rather than returning a modified one. In most (if not all)
Lisp implementations, small integers are not implemented as structs,
but you can only know this by observing allocation meters; there
isn't an "in language" way to figure out that integers are not implemented
as structs. And of course, bignums work exactly the way I described;
(setf (ldb (byte 8 8) a-bignum) 25) does not modify the struct that
represents the bignum.
This little trick I play on myself works because Lisp has no way
to explicitly create pointers, or to dereference those pointers that you
can't create anyway.
Somebody long ago -- maybe Dan Weinreb -- referred to this as "call
by value-reference", and that's what I still think of it as.
> Duane Rettig <du...@franz.com> writes:
>
> > I've also disliked the usage of the term "tagged pointer" to describe
> > the words which describe Lisp objects, for the same reason (pointer
> > implies address, which doesn't exist for an immediate object). So I
> > have always described these tagged words as LispVals, and thus by using
> > a completely made-up word I avoid the preconception of whether a
> > LispVal is a pointer or a value (it is both and it is none). And if
> > one accepts the made-up and unencumbered term LispVal, it becomes
> > obvious that Lisp is "pass by LispVal".
>
> Not the sort of term you'd expect any other language to adopt. ;)
> I'd accept "by object"...? LispVal is a foreign term to anyone who uses
> the language. It also suggests that it can't be used for any other language,
> when in fact I think it should be usable by other languages.
Right. As I said in another post, I would be willing to have a better
term suggested than LispVal, one which is more general than pertaining
only to Lisp. However, if you accept "by object", are you ready for all
of the overloading which _that_ term has on it? :-)
> Aren't you just redefining the meaning of pass-by-value here? Is there
> any system that isn't pass-by-value under your definition?
Yes. Pascal, for example, supports pass-by-reference variables. C,
however, does not; C is also a pass-by-value language.
> Would it be wrong to say that CL is pass-by-reference, with the
> exception of numbers and characters, which are pass-by-value?
Yes, that would be wrong.
> No. I am using the usual meaning of call-by-value. And, yes, there
> are systems that do not "pass-by-value". Examples are Haskell and
> Miranda (call-by-need aka lazy evaluation), Algol (call-by-name),
> Pascal and friends (including C++) where you can declare parameters as
> call-by-reference, Ada (can declare parameters as call-by-copy-in-out).
> Maybe there are more.
Hey, how about the grand-daddy of them all! FORTRAN is traditionally
call-by-value-result. However, in FORTRAN 77 (I don't know about the
more recent standards), implementations were allowed to use
call-by-reference instead, and compatible programs are not allowed to
do anything that might tell the difference.
(I assume that what I was taught to call call-by-value-result is what
you say Ada does with call-by-copy-in-out.)
Thomas
It's easiest if you think of *everything* as being pass-by-reference.
Numbers and characters are immutable objects which means there are no
operations that allow you to change their value. So there isn't any way
that you can find out whether they are passed by reference or by value.
-- Bruce
> C books that explain things correctly exist.
Really? Would you be so kind as to cite a few?
> Me writing one more would not have made a difference.
How do you know?
> Or are you trying to say that I
> should write a couple of hundred "correct" C books to give "correct" C
> books the majority? :-)
No need to write a couple of hundred, but it should be a number greater
than zero if you want to claim that it's not your fault that people don't
know this stuff. People don't know this stuff because they read
Stroustrup's book instead of yours, because his exists and yours doesn't.
That's your fault. Or maybe they read Stroustrup instead of someone
else's book because you never got around to telling anyone which books are
the ones that "explain things correctly." That's your fault too.
> > > > Yes, but that's not nearly as easy to do as the brevity of your
> > > > description implies. I didn't say it was impossible, I just said
it was a
> > > > substantial amount of work. All you've done is give a brief description
> > > > of the work.
> > >
> > > Well, I have done this work several times over. Trust me, it is
> > > really not hard at all compared to many other things that come up when
> > > implementing HLLs.
> >
> > I believe that. But just because it isn't hard relative to something else
> > doesn't mean it's not "a substantial amount of work."
>
> When I wrote the three lines above, I somewhat expected you to come
> back with this reply. Unfortunately, I resisted my urge to say "It is
> dead easy." -- which is what I originally intended to write. And yes,
> it *is* dead easy. In fact, I don't see at all why you think that
> there would be any problem whatsoever.
I didn't say it was a problem. I didn't say it wasn't easy. I just said
it was a substantial amount of work. Many things in C are neither
problematic nor hard but are nonetheless a substantial amount of work, at
least by comparison with Lisp. (Creating complex linked data structures,
for example.)
You could prove me wrong by posting some code that shows how it's done.
If it's as dead-easy as you say it shouldn't take you very long.
E.
> Jacek Generowicz <jacek.ge...@cern.ch> writes:
>
> > In what sense do you mean that "as in Lisp" no copy is made ? In
> > Common Lisp, when passing objects (with the possible excepiton of
> > numbers and characters), no new instance (copy) is made; in (C and)
> > C++ there is (as can be seen above).
>
> Read my reply to the OP
You appear not to have repiled to the OP. I'm not prepared to trawl
through all your other replies trying to guess which one you mean.
> who posted the same irrelevant (since besides-the-point) stuff.
Beside which point ? The name of this thread is `quest for
pass-by-reference semantics in CL'. Such a quest is motivated by the
misunderstaning of what happens when Lisp passes by value. If
anything is beside the point, then it is your insintence that it does,
strictly speaking, pass by value.
elsewhere Matthias Blume <matt...@shimizu-blume.com> writes:
> There simply is no Lisp equivalent of a C variable of struct type.
Fine, use the (exteremely relevant) example of a C++ object. The OP
wanted pass-by-reference which doesn't even exist in C.
> A variable holding a struct in Lisp is equivalent to a C pointer to
> a C struct.
Equivalent in what sense? In the latter one has to faff around with
explicitly dereferencing the pointer to get at the interesting data,
in the latter one does not. Your equivalence relation strikes me as
irrelevant.
> In C and in Lisp, when using a variable as the actual argument to a
> function call, the bits in all the locations denoted by that
> variable get copied into a fresh location denoted by the formal
> parameter of the function.
Define `denoted'. I can't find an entry for `denote' in the
Hyperspec. The Oxford English Dictonary gives (among less relevant
things) the following:
To signify; to stand for as a symbol, or as a name or expression
Consider (setq x (list "hello" (make-instance 'blark)) ... ).
You appear to be suggesting that "hello" and the instance of blark
will get copied to fresh locations when x is passed as an argument to
a function.
> It just so happens that in Lisp, the number of locations so copied
> is always 1 while in C it can be greater than 1.
> A "value", btw., is just those "bits" without the location that stores
> them.
How do you reconcile this with the preceding two lines ?
Specifically, how does any arbitrarily complex, compound lisp object
get passed by value satisfying both your conditions:
a) no more that 1 location is copied,
b) the locaton that stores the object is not part of the value
?
> In contrast, call-by-reference does not transmit the bits.
> Instead, it transmits the location(s)
Sounds very much like what happens in Lisp: the bits making up the
"hello" and the blark instance are not transmitted.
If you really wanted to carry your argument ad absurdum, then I'm sure
you could argue that pass-by-reference doesn't exist anywhere, because
ultimately, somewhere in the implementation a value representing the
reference is passed.
> Duane Rettig <du...@franz.com> writes:
>
> > Matthias Blume <matt...@shimizu-blume.com> writes:
> >
> > > Duane Rettig <du...@franz.com> writes:
> > >
> > > > In C, pass-by-value implies that a copy is made of the value passed,
> >
> > > Nonsense!!
> >
> > Of course. How can I argue with such authority?
>
> Right, I was wondering, too.
>
> > > In C, as in Lisp, no copy is made.
> >
> > You should check your facts. I agree that in Lisp no copy is made
> > (except in the actual transfer of the always-one-word LispVal from
> > caller to callee). But C is allowed to copy small structs, as part
> > of the pass-by-value strategy. And most other C values are one-word
> > values, and as such they are copied or transferred, however you
> > wish to call it.
>
> I know my facts. See my answer to Erann's post.
OK, here are a few items I pulled off the web to lend credence
to my "Nonsense":
===
Brian W. Kernigan (1974) "Programming In C: A Tutorial"
http://www.lysator.liu.se/c/bwk-tutor.html#functions
"Simple variables (not arrays) are passed in C by ``call by value'',
which means that the called function is given a copy of its
arguments, and doesn't know their addresses."
(Here, Kernigan is defining call-by-value, where copying is
explicitly prescribed.)
===
comp.lang.c Frequently Asked Questions:
http://www.eskimo.com/~scs/C-faq/q2.7.html
Question 2.7: "I heard that structures could be assigned
to variables and passed to and from functions, but K&R1
says not."
Answer: "What K&R1 said was that the restrictions on structure
operations would be lifted in a forthcoming version of the compiler,
and in fact structure assignment and passing were fully functional
in Ritchie's compiler even as K&R1 was being published. Although a
few early C compilers lacked these operations, all modern compilers
support them, and they are part of the ANSI C standard, so there
should be no reluctance to use them.
(and the footnote: http://www.eskimo.com/~scs/C-faq/fn1.html):
"However, passing large structures to and from functions can be
expensive (see question 2.9), so you may want to consider using
pointers, instead (as long as you don't need pass-by-value
semantics, of course)."
(Here, the structure passing style is clearly labelled pass-by-value.)
===
comp.lang.c Frequently Asked Questions:
http://www.eskimo.com/~scs/C-faq/q2.9.html
Question 2.9: "How are structure passing and returning
implemented?"
Answer (for the passing half): "When structures are passed
as arguments to functions, the entire structure is typically
pushed on the stack, using as many words as are required.
(Programmers often choose to use pointers to structures
instead, precisely to avoid this overhead.) Some compilers
merely pass a pointer to the structure, though they may have
to make a local copy to preserve pass-by-value semantics.
(Again, pass-by-value is clearly distinguished from explicit
passing by reference).
===
http://www.thunderstone.com/site/vortexman/node24.html
http://www.juicystudio.com/tutorial/c/passbyvalue.html
http://www.cs.uakron.edu/~xiao/ics/Lab07.html
First paragraph in each of these references say it all.
===
http://www.megalink.net/~ccs/portable-cpp.htm#copy_constructors
(Here's a C++ reference which strongly encourages creating copy
constructors for classes you define, explicitly because passing
by value _means_ calling the copy constructor!)
===
I believe that my case is not to prove to you what "pass-by-value"
means - it is going to mean to you whatever you want it to
mean. What I do hope you'll start to understand is that
"pass-by-value" has multiple meanings, and that such multiple
meanings cause problems for Lisp and languages like it. That is
why Kent and I each coin and search for more descriptive (or less
overloaded) terminology to describe what goes on. The semantics
of Lisp's calling style shouldn't need all of the baggage that
"pass-by-value" carries with it.
> Kent M Pitman <pit...@world.std.com> writes:
>
> > Duane Rettig <du...@franz.com> writes:
> >
> > > I've also disliked the usage of the term "tagged pointer" to describe
> > > the words which describe Lisp objects, for the same reason (pointer
> > > implies address, which doesn't exist for an immediate object). So I
> > > have always described these tagged words as LispVals, and thus by using
> > > a completely made-up word I avoid the preconception of whether a
> > > LispVal is a pointer or a value (it is both and it is none). And if
> > > one accepts the made-up and unencumbered term LispVal, it becomes
> > > obvious that Lisp is "pass by LispVal".
> >
> > Not the sort of term you'd expect any other language to adopt. ;)
> > I'd accept "by object"...? LispVal is a foreign term to anyone who uses
> > the language. It also suggests that it can't be used for any other language,
> > when in fact I think it should be usable by other languages.
>
> Right. As I said in another post, I would be willing to have a better
> term suggested than LispVal, one which is more general than pertaining
> only to Lisp. However, if you accept "by object", are you ready for all
> of the overloading which _that_ term has on it? :-)
Oh sure. At least here I've got the moral high ground. And besides,
it matters a lot more to make the term meaningful within Lisp than for
people who want to compare languages. The essence of what I hear
Matthias Blume saying is basically just that I should begin from the
terms others use outside, and I reject that premise. They have made
no effort to begin with terms that are familiar to us, and we're a
very old language community that pre-dates those things which are
asserting a right to dictate terminology... I say we make the best of it
and leave the comparative linguists (which include myself btw, on other
days) to fend for themselves.
Btw, I went to a meeting (perhaps the first meeting) of X3H7 which was
going to address this issue. It seemed to be an attempt to transplant
some CORBA group to ANSI rules, and I found it a little troubling
because it kind of had a pre-dictated agenda that was hard for
outsiders to affect, so I declined to attend subsequent meetings, not
wanting to waste my time. But one thing we did for that meeting was
go around the room and ask people what an object was. The answers
were revealing. One person said, "I think an object is like--like--a
database." Another said, "I think an object is like a satellite. A
satellite is an object isn't it?" Weird stuff like that. Very
touchy-feely. I said "I think an object is something you can pass to
a function as an argument in a function call." There was spontaneous
laughter when I said this. I think they thought I was kidding. My
answer was ridiculously concrete compared to the others people were
offering... but that was the point. And again, this is the yin to the
yang of the other point: _of course_ Lisp is call by object/identity,
since what you want to do in Lisp is give objects (or object
identities) as arguments to functions, and have those objects still be
themselves when they arrive on the other side. The pass-through of the
argument is supposed to be an identity transformation!
Then again, maybe the real problem is not over the function calling but that
we have lost the war on what identity means. If so, that's very sad.
> In article <877kml6...@becket.becket.net>,
> tb+u...@becket.net (Thomas Bushnell, BSG) wrote:
>
> > Frode Vatvedt Fjeld <fro...@acm.org> writes:
> >
> > > Would it be wrong to say that CL is pass-by-reference, with the
> > > exception of numbers and characters, which are pass-by-value?
> >
> > Yes, that would be wrong.
>
> It's easiest if you think of *everything* as being pass-by-reference.
It may be "easiest", but it is also completely wrong.
Notice that "pass-by-value" (which is the only correct answer to the
question) does not imply that the caller cannot change parts of a data
structure. The whole question is *only* about how the formal
parameter of the function (i.e., within the caller) is related to the
actual argument given at the callsite.
Call-by-reference, otoh, would mean that in
(defun f (x) (setq x 2))
(defun g (y) (f y) y)
(g 1)
the final answer from calling g would have to be 2. Replace 2 with
(CONS 2 '()) and 1 with (CONS 1 '()), and you see a result of (1), not
(2). So the problem is unrelated to what data you send to the function.
Matthias
> > who posted the same irrelevant (since besides-the-point) stuff.
>
> Beside which point ? The name of this thread is `quest for
> pass-by-reference semantics in CL'. Such a quest is motivated by the
> misunderstaning of what happens when Lisp passes by value. If
> anything is beside the point, then it is your insintence that it does,
> strictly speaking, pass by value.
It does, there is nothing I or you could do about it right now.
Whether or not it is irrelevant is another question. I don't think it
is, though.
> > There simply is no Lisp equivalent of a C variable of struct type.
>
> Fine, use the (exteremely relevant) example of a C++ object. The OP
> wanted pass-by-reference which doesn't even exist in C.
There is no equivalent of a C++ variable of class type in Lisp. A
variable holding an object in Lisp would have to be modeled by a
pointer to a value of class type in C++.
> > > A variable holding a struct in Lisp is equivalent to a C pointer to
> > a C struct.
>
> Equivalent in what sense?
In the sense that if you write down a variable holding such a thing as
an actual argument of a function and the function writes to the formal
parameter corresponding to this argument, you will not see the change
in the caller. On the other hand, changes to the state of the object
itself are visible. The first fact shows that argument passing is
by-value, the second shows that objects in Lisp have reference
semantics (generally, this is not tied to the problem of argument
passing). The only way I know of to achive both in C or C++ is to use
pointers to structs/objects.
> In the latter one has to faff around with
> explicitly dereferencing the pointer to get at the interesting data,
> in the latter one does not. Your equivalence relation strikes me as
> irrelevant.
That's because the deref op is built into the respective Lisp
operations. This is just like C's or C++'s -> operator which also has
a built-in deref. (As a matter of fact, in C++ you do *not* have to
write explicit deref ops because the field accessor ".", when applied
to a pointer, ends up behaving like ->.)
> > In C and in Lisp, when using a variable as the actual argument to
> > a function call, the bits in all the locations denoted by that
> > variable get copied into a fresh location denoted by the formal
> > parameter of the function.
>
> Define `denoted'.
It is a standard term in PL semantics.
> Consider (setq x (list "hello" (make-instance 'blark)) ... ).
>
> You appear to be suggesting that "hello" and the instance of blark
> will get copied to fresh locations when x is passed as an argument to
> a function.
No, I am not at all suggesting that. The variable x denotes a single
location which is not one of those (several) locations that constitute
the CONS cells (which in turn make up the list), or which hold the
characters in the string, or which make up the blark instance. Under
cbv semantics, only the bits stored in that one location denoted by x
get copied.
> > It just so happens that in Lisp, the number of locations so copied
> > is always 1 while in C it can be greater than 1.
>
> > A "value", btw., is just those "bits" without the location that stores
> > them.
>
> How do you reconcile this with the preceding two lines ?
> Specifically, how does any arbitrarily complex, compound lisp object
> get passed by value satisfying both your conditions:
I have explained this already. This is because "arbitrarily complex,
compound Lisp objects" are constructed from many individual values,
most of them containing _locations_. Locations, in turn, are mapped
by what's usually called the "store" to other values.
Example: (LIST 1 2) consists of 5 values, 2 of them numbers, two of
them cons cells, one is the atom NIL. Cons cells contain two
locations each. The first location in the first cell is mapped to 1,
the second is mapped to the second cell, the first location of the
second cell is mapped to 2, the second location is mapped to NIL. Any
updates to locations in this data structure leave all 5 values alone,
they merely change the glue -- the mapping from locations to values.
When you pass such a structure, you really only pass the root value
around. All the others get "dragged along" by virtue of the glue.
> a) no more that 1 location is copied,
> b) the locaton that stores the object is not part of the value
I see no problem whatsoever with these 2.
> > In contrast, call-by-reference does not transmit the bits.
> > Instead, it transmits the location(s)
>
> Sounds very much like what happens in Lisp: the bits making up the
> "hello" and the blark instance are not transmitted.
But these bits are not the ones denoted by X! If you do (setq x '())
you still have the same variable x, and it still denotes the same
location as before, but now there is no trace of a relationship
between it and your list data structure.
> If you really wanted to carry your argument ad absurdum, then I'm sure
> you could argue that pass-by-reference doesn't exist anywhere, because
> ultimately, somewhere in the implementation a value representing the
> reference is passed.
No, I would not argue that way. But you are right insofar as it is
indeed true that call-by-reference tends to get implemented internally
by passing addresses around -- which could be expressed in an
implementation's intermediate language by making these addresses
explicit (and then passing them by value).
Matthias
I don't think its a war that has been lost, but a lack of appreciation
of the concept. In C/C++/VB etcetera, you're always thinking in terms
of "by value" or "by reference" and dealing with consequences of
fussing about with how the parameter is used. I think the complexity
of this tends to blind one to the simplicity that call-by-identity
offers. I asked you about this very thing some time ago and it took
me a while to begin to "get it", mostly because the idea seemed too
simple in some way.
Gregm
> > It's easiest if you think of *everything* as being pass-by-reference.
>
> It may be "easiest", but it is also completely wrong.
>
> Notice that "pass-by-value" (which is the only correct answer to the
> question) does not imply that the caller cannot change parts of a data
> structure. The whole question is *only* about how the formal
> parameter of the function (i.e., within the caller) is related to the
> actual argument given at the callsite.
>
> Call-by-reference, otoh, would mean that in
>
> (defun f (x) (setq x 2))
> (defun g (y) (f y) y)
> (g 1)
>
> the final answer from calling g would have to be 2.
?? No it would not.
Here's the equivilent C code (with types assumed to be integers for
simplicity):
#include <iostream.h>
int* f(int *x){return x = new int(2);}
int* g(int *y){f(y); return y;}
int main(){
cout << *g(new int(1)) << endl;
return 0;
}
This prints "1", just as any Lisp does (CL, elisp, scheme, dylan).
As I said, an appropriate model for thinking about Lisp programs is that
all values live on the heap and variables and function arguments contain
references to those values.
The only real modification you need to the above for full Lisp
compatability is that you need to assume an "operator new" for integers
that returns the same object each time it is called with the same
argument.
> Replace 2 with (CONS 2 '()) and 1 with (CONS 1 '()), and you see a
> result of (1), not (2). So the problem is unrelated to what data you
> send to the function.
That's right, except that there is no problem.
-- Bruce
"eq. The program for eq[e; f] involves testing for the numerical
equality of
the locations of the words. This works because each atomic symbol has only
one association list."[1]
according to which, lisp is a language in which all argument values are
locations. that is, lisp is "call-by-location". while this may well be a
special case of "call-by-value", that term is not, of itself, adequate
to express either that all values are locations, or that a "variable" in
lisp does not name a storage location which can contain values, but
names a location which contains a binding. it is not significant that
some locations may be distinguished, may be immutable, or, as an aspect
of a given implementation, may even not be associated with any register
in the store. the initial implementation already made such
distinctions.[thus the second sentence in the above citation, and 2]
the original description also already explains why, although it is
possible to emulate call-by-reference, it is not a first-class feature,
but is available only implicitly through operations on closures:
"2. eval[e; a] has two arguments, an expression e to be evaluated, and a list
of pairs a. The first item of each pair is an atomic symbol, and the
second is
the expression for which the symbol stands.
3. If the expression to be evaluated is atomic, eval evaluates whatever is
paired with it first on the list a."[3]
in terms of which call-by-reference would entail admissibility of the
word which represents a variable binding as an argument value. in other
words, there is no intrinsic call-by-reference since there is no
language construct which effects a binding to a binding.
as an aside, this terminology even offers the advantage unifying
common-lisp with other lisp dialects.
...
Duane Rettig wrote:
>
> Kent M Pitman <pit...@world.std.com> writes:
>
> > Duane Rettig <du...@franz.com> writes:
> >
> > > I've also disliked the usage of the term "tagged pointer" to describe
> > > the words which describe Lisp objects, for the same reason (pointer
> > > implies address, which doesn't exist for an immediate object). ...
> >
> > Not the sort of term you'd expect any other language to adopt. ;)
> > I'd accept "by object"...? LispVal is a foreign term to anyone who uses
> > the language. It also suggests that it can't be used for any other language,
> > when in fact I think it should be usable by other languages.
>
> Right. As I said in another post, I would be willing to have a better
> term suggested than LispVal, one which is more general than pertaining
> only to Lisp. However, if you accept "by object", are you ready for all
> of the overloading which _that_ term has on it? :-)
>
Erik Naggum wrote:
>
> I think we need a fresh start.
>
> Argument passing in Common Lisp is conceptually equivalent to building
> a list of the result of evaluating each of the argument forms to the
> function and passing that list to the function, which conceptually
> unpacks it into the formal parameters. All knowledge of the source of
> the values is lost by that time.
>
> The conceptual equivalence to a list is used to lay the ground for apply,
> &rest, &key, etc, and also neatly captures the order of evaluation so an
> explanation of this rule will fall out naturally from the description.
--------------------------
[1] "Recursive Functions of Symbolic Expressions and Their Computation
by Machine, Part I"
#1=(http://www-formal.stanford.edu/jmc/recursive.pdf) p.28
[2] #1# p23, p25
[3] #1# p18
> > The lisp program is not equivalent to the
> > C program. There are these particular differences:
> >
> > 1. The C code allocates a struct on the stack, the Lisp code
> > allocates it in main memory. So you'd need to call malloc
> > in the C code.
> >
> > 2. C allows you to pass structs in argument lists. The way
> > this works is that the struct is `destructured' at the call
> > site, and `restructured' at the callee.
>
> While I am always happy to know about things like this for my own
> well-roundedness I can tell you that as a user of a language the above is
> not relevant *at all* to understanding what will happen when I write
> "foo(bar);" in C and when I write "(foo bar)" in lisp. That kind of stuff
> is not for users it is for compiler writers (I have great respect for people
> who write compilers, but no desire be one).
Au contraire. As a user of a programming language one should have a
very deep understanding of the semantics of that language.
> The fact that such contortions as you went through below in lisp and similar
> contortions one would have to do with pointer dereferencing to satisfy
> Mathias' objections in C, the fact that these contortions are necessary to
> produce equivalent programs is very strong evidence that the languages do
> behave differently.
But not as far as call-by-value parameter passing is concerned. The
differences are in what kind of values and what kind of variables exist.
> If the text books all agree that C and Lisp are both call-by-value then that
> is what they say and it is pointless to dispute. But names are supposed to
> disambiguate between concepts,
They do. The concept is the same here, as far as parameter passing is
concerned.
> and there are clearly two distinct concepts
> here. Too claim that they are the same merely because the have the same
> label is foolish.
No, they have the same label because they are conceptually the same.
--
-Matthias
> In article <m33cxad...@hanabi.research.bell-labs.com>, Matthias Blume
> <matt...@shimizu-blume.com> wrote:
>
> > C books that explain things correctly exist.
>
> Really? Would you be so kind as to cite a few?
Harbison & Steele.
> No need to write a couple of hundred, but it should be a number greater
> than zero if you want to claim that it's not your fault that people don't
> know this stuff. People don't know this stuff because they read
> Stroustrup's book instead of yours, because his exists and yours doesn't.
I am not in the business of writing C or C++ books. I cannot take the
blame for every ommision and mistake of others just because I have not
filled in the gap or provided the correction. Or are you going to
blame me for world hunger next?
> I didn't say it was a problem. I didn't say it wasn't easy. I just said
> it was a substantial amount of work.
But it isn't.
> You could prove me wrong by posting some code that shows how it's done.
> If it's as dead-easy as you say it shouldn't take you very long.
Write
struct foo * f (struct bar * x) ...
where you would have written
struct foo f (struct bar x)
Then replace occurences of "x." with "x->".
(In C++ you don't even have to do the latter, IIRC.)
This move from struct variables to "pointer to struct" variables must
be applied, mutatis mutandis, to other parts of the program as well.
But doing so is trivial.
--
-Matthias
> > > C books that explain things correctly exist.
> >
> > Really? Would you be so kind as to cite a few?
>
> Harbison & Steele.
Harbison & Steele states "C provides only call-by-value parameter
passing. This means that the value of the actual parameters are
conceptually copied into a storage area local to the called function."
<Sec 9.5>. This behavior is different from that of Common Lisp.
--
"What we hear constantly is that after September 11th, everything changed.
There is a good rule of thumb: if something is repeated over and over as
obvious, the chances are that it is obviously false." -- Chomsky
<http://www.zmag.org/content/ForeignPolicy/chomsky_march26.cfm>
FYI, this is C++, not C. Anyway, you see the same effect as in Lisp
because your code uses CALL-BY-VALUE, just like Lisp (and C) does!!
Try declaring x as &x.
> As I said, an appropriate model for thinking about Lisp programs is that
> all values live on the heap and variables and function arguments contain
> references to those values.
No, variables are values that hold *pointer*. There is a fine
distinction between these two things.
--
-Matthias