passing by refference

Doug Quale quale1 at charter.net
Fri May 30 02:38:05 EDT 2003


Tim Peters <tim.one at comcast.net> writes:

> [Doug Quale]
> > (I had looked up some of your earlier posts on this issue after I
> > entered this morass.  Google says that "call-by-object-reference" is
> > most popular recently in reference to Python, often in threads that
> > you have participated in in this group.  The term is not unknown in
> > published computer science research, but it's rare.)
> 
> Sorry, I didn't mean a literature search on call-by-object-reference, I
> meant a literature search on call-by-value.  You can't come away from such a
> search still believing there's a single "true meaning" for that phrase.  Or,
> if you can, there's no hope for you <wink>.

Real life intruded for a couple of weeks, which is just as well
because I don't have much more to say about this.  I will manage one
final overly long and repetitive post.  There's far too much
repetition in what I say and have said, but since I don't have any
more to say after this I'll leave it in.

My point was simply that call-by-object-reference and call-by-sharing
are terms that are not commonly used by experts in the field of
programming language theory.  They are not unknown, but they are
rather rarely used by experts.  If anyone disputes that I am
interested in evidence to the contrary.  (The CLU docs and Henry
Baker's paper are good examples.  I will have a little more to say
about each of them below.)  I suspect that experts don't use those
terms because they don't say anything interesting to an expert.  They
can certainly be helpful for others.  It is no more necessary to be an
expert in Theoretical CS to be a good programmer than it is necessary
to be an expert in the theory of electromagnetic fields to repair a
radio.  On the other hand if you want to learn theoretical physics you
might choose to talk to the expert in theoretical physics rather than
the radio repairman.  (Once upon a time, you could have talked to both
at the same time by asking Richard Feynman.)

> Even the FOLDOC definition people started with here:
> 
>     changes to the arguments within the called procedure have no
>     effect on the actual arguments as seen by the caller
> 
> doesn't match your preferred meaning, except by virtue of extreme
> logic-chopping about what "no effect on the actual arguments" means.  I'm
> afraid I think it's plainly unreasonable to claim that, when I do
> 
> x = [1, 2, 3]
> f(x)
> print x
> 
> and see [-1, -1, -1], the f(x) call can be said to "have no effect on the
> actual arguments as seen by the caller".  To get away with that trick
> requires stripping words of all reasonable meaning.  BTW, the FOLDOC
> definition does match the most widely accepted meaning for call-by-value
> from my college days (although there was more than one meaning in common use
> then too).  I believe the phrase has been "elaborated" so much in succeeding
> years that a different meaning is more common now, and the phrase is
> correspondingly less useful than it once was.

The FOLDOC definition is confused.  First, let me repeat one last time
the correct definition.  Note that this definition is shorter and more
precise than that given by FOLDOC.  In call-by-value, the actual
parameters are evaluated and their r-values are passed to the called
procedure.  One sentence; that's all there is to it.  Now on to the
FOLDOC confusion:

    Call by value
    (CBV) An evaluation strategy where arguments are evaluated before
    the function or procedure is entered. Only the values of the
    arguments are passed and changes to the arguments within the
    called procedure have no effect on the actual arguments as seen by
    the caller. See applicative order reduction, call-by-value-result,
    strict evaluation, call-by-name, lazy evaluation.

FOLDOC starts talking about an evaluation strategy, and the definition
used is the same one that is often used in the functional programming
language community.  By this definition Python is indeed call-by-value
since arguments are evaluated before the function is called.  In
general I think the other terms FOLDOC mentions like applicative and
strict are more appropriate to use in the context of evaluation
strategy.  (You mentioned precisely this point in an earlier post.  I
would wager that few people reading this newsgroup think of
call-by-value when applied to Python as describing applicative order
evaluation.  Relatively few programmers have much exposure to lazy
languages.  Python, however, has made good use of the idea of list
comprehension from the functional programming community.)

More interesting is what call by value means as an argument passing
strategy since the requirement that arguments be evaluated before the
functions is called doesn't distinguish between call-by-value and
call-by-reference.  The FOLDOC entry is strongly oriented towards the
functional programming view of the meaning of call-by-value, but it
goes on to say that values of the arguments are passed.  This is true
of call-by-value in any context.  Unfortunately FOLDOC jumps the
tracks with the very next bit when it says that the called procedure
can have no effect on the actual arguments.  This is wrong, or at
least extremely misleading.  If the values that are passed are mutable
objects, then of course those objects can be changed.  The argument
will still refer to the same object, but the object itself will have
mutated.  This shows up even in C.  It is true that a very careful and
precise meaning can be attached to the term "arguments" to make it
technically true that arguments can't be changed by the callee in
call-by-value, but it's a grave mistake to tread on that slippery
ground since it just begs for confusion.

The FOLDOC definition is so close.  The only problem is that it
actually tries to say too much.  If it omitted the red-herring about
not changing argument values, it would be spot on.  I think many
people think that that's a necessary consequence of passing values
instead of references, but they're wrong.

Although you correctly said that C structures are passed by value in
an earlier post, that simply doesn't guarrantee the behavior that you
and FOLDOC think it does.  Here is a contrived but simple example.

#include <stdio.h>

struct s {struct s *self; int i, j, k;};

void display(const char* tag, struct s x) {
  printf("%s %d %d %d\n", tag, x.i, x.j, x.k);
}

void f(struct s x) {
    display("inside before", x);
    x.self->i = x.self->j = x.self->k = -1;
    display("inside after", x);
}

int main() {
  struct s t = {&t, 1, 2, 3};
  display("before", t);
  f(t);
  display("after", t);
}

Which prints

before 1 2 3
inside before 1 2 3
inside after -1 -1 -1
after -1 -1 -1

So C structures don't have the much cherished "no effect" properties
either.  On the other hand, I've never seen anyone claim that C
structures are not passed by value.  (I suppose someone will pop up to
dispute that now.)  By the FOLDOC definition, neither C nor Pascal is
call-by-value.  (Pascal of course has var parameters, but even Pascal
value parameters would not be call-by-value by FOLDOC's confused
definition.)

> > I'm afraid I can't determine the meaning of technical terms like
> > "call-by-value" by what any normal person thinks it means.  Like any
> > specialized technical terminology it means what expert practicioners
> > in the field agree it means.
> 
> And when they don't agree on a single definition?  They don't in this case,
> but it's not a religious issue to them either, and neither is it a barrier
> to communication when the differences are understood.

Actually I gave you the canonical definition.  FOLDOC is interesting
but it doesn't have the weight that the Dragon Book has (and well
deserves).  The CLU references don't actually define c-b-v.  They
state that CLU is not call-by-value because it has a particular
property that they claim c-b-v cannot have.  The CLU people provided
great benefit to the study of object oriented languages, but they were
simply wrong about the consequences of call-by-value.

There's absolutely no reason to insist that call-by-value must have
the property that objects passed as values must be immutatable.  It's
a myth that it this would be an even remotely desirable property of
something called "call-by-value".  In practice, you almost never want
this.  The only languages that could be call-by-value by that
definition are pure functional languages and possibly some languages
that perform deep copies of all arguments.  Deep copies are not common
because they are not desirable for either efficiency or utility.
Avoiding deep copies is not just an efficiency hack -- the
expressiveness languages like Java and Python would be crippled by
mandatory deep copies of arguments.  In Pascal copies can be avoided
by using var parameters.  C defaults to not copy arrays and pointers
can be used to defeat copying of structs and other types.  Java and
Python don't have explicit reference operators so it's essential that
objects passed as arguments can be mutated in these languages.  If it
were otherwise these languages would lose most of their expressive
power.

As it happens people are almost never surprised when a user type is
mutated; it's only certain builtin types like lists that cause
confusion.  I doubt any programmer would ever be surprised by this

>>> def increment_count(d, x):
>>>     d[x] = d.get(x, 0) + 1
>>> y = {}
>>> increment_count(y, 'a')
>>> y
{'a': 1}

or would have any trouble with the explanation that the value of y (a
dictionary) is passed and mutated.  This is the same as the list
example you gave before.  Only the nature of the type of value passed
makes it less likely that any programmer would be would think it
unusal that the passed value could be mutated.

> >> Even so, in no case does it have enough explanatory power to answer
> >> most practical questions about the semantics of calls in Python.
> 
> > What practical question can it not explain about calls in Python?  It
> > very easily explains the example you gave before.
> 
> Actually not:  virtually all of the explanation lied in dragging in r-value
> and l-value concepts.  Occam's Razor is what opposes that:  all arguments in
> Python are passed by object identity, some objects are mutable, others
> aren't, and an explanation built on those terms is *useful* in Python life.
> You won't hear about r-values and l-values again in Python, so it's not
> helpful here to drag them in.  Of course if you were trained to think of
> everything in terms or r- and l-values, it would be helpful to you -- but
> most people haven't been so trained.

I don't deny that most people aren't familiar with the terms l-value
and r-value.  This does make it difficult to see how these same people
then determine whether a language is c-b-v since the concept of
r-value is integral to the definition of call-by-value.

Of course in practice anyone who understands Python actually knows
what an r-value is in Python even if they don't use the term, since
you need that to understand assignment.  Even if Python functions took
no parameters at all exactly the same issues occur when you explain
why the value of y gets mutated in

>>> def f():
>>>     x[:] = [-1] * 3
>>> y = [1, 2, 3]
>>> x = y
>>> f()
>>> del x
>>> y
[-1, -1, -1]

and y is never passed and still mutates.

Alternately,

>>> def f(x):
>>>    x[:] = [-1] * 3
>>>
>>> y = [1, 2, 3]
>>> z = y
>>> f(y)
>>> z
[-1, -1, -1]

and z is assigned only once, never passed, but still mutates.

Since r-values are necessary to explain assignment, I wouldn't say
that they are dragged in to explain argument passing.  Once you've
explained assignment in a call-by-value language you've already
explained the values used in argument passing too.

(Actually assignment is harder to explain than argument passing since
assignment brings l-values into the picture and these are usually more
complex than r-values.  This is true in Python for instance, since
l-values such as x[:] take a little explaining.  The formal parameters
involved with argument passing are l-values of a sort, but since they
are generally just variables (names) they are the very simplest kind
of l-value.)

As Joshua pointed out earlier, Python has no explicit dereferrencing
so there's no essential difference in Python between an object and an
object reference.  This makes Python fundamentally simpler (and more
sane) than C++, so we need never talk about object references in
Python at all if we don't want to.

y = [1, 2, 3]

Variable y is bound to a list object.

f(y)

The value of y is passed to function f.  The value of y is a list, so
that list is passed.  From the definition of f,

def f(x):
   x[:] = [-1] * 3

formal parameter x is bound to the passed value which in this case is
the list that y names.  At this point we have two names for that list.
The list is mutated.  When we return we find that y is still bound to
the same list it was bound to before, but the contents of that list
have been changed.  This is all vanilla call-by-value.  No mention of
l-values, r-values or object references.  There were some complaints
on the newsgroup before about how considering Python to be c-b-v would
require maintaining that Python passes object references rather than
objects themselves.  As you can see this is not true.

> > Argument passing in Java and C are no different from Python.
> 
> Given the meanings you insist that words have, possibly so.  It's an easily
> observable fact that people coming from C have a hard time learning Python's
> calling semantics, though, so that should give you pause about whether
> you're constructing a useful model of reality.  Python and Java are indeed
> close, and people moving from C to Java have a hard time learning Java's
> calling semantics too; people moving between Python and Java don't stumble
> over this.

I understand why people would see a greater difference between
argument passing between C and Python than between Java and Python.
The truth is that this has nothing to do with the argument passing and
everything to do with the nature of values in those languages.  C
r-values aren't very much like Python r-values.  Java r-values are
very similar to Python r-values.  This shows up in assignments every
bit as much as in argument passing.  If you insist that argument
passing is different in C and Python then you must also insist that
assignment is different.  It's much more simple and sane to realize
that the nature of r-values is different and argument passing and
assignment are the same.

> > The same mutation that you showed in your Python example occurs there
> > too.
> 
> It depends on whether a pointer or structure is passed in C.  There's no
> such distinction in Python.

In C, r-values are more complex (and ugly).  C r-values aren't
consistent, so arrays and structures get treated differently.  The
r-value of a struct is a copy of the struct, the r-value of an array
is the address of the array.  In the end, it's still an r-value that
gets passed just like it's an r-value that gets assigned.  The
behavioral difference between C structures and pointers has nothing to
do with argument passing.  It happens in C assignments too.  It's the
nature of r-values of C types.

Python r-values are much more simple, but confusion does arise because
some Python values like integers are immutable and others like lists
are mutable.

> > Java and C are both usually considered to be call-by-value, but you
> > seem to have a different opinion.
> 
> You're not hearing:  I'm not arguing for any specific meaning of
> call-by-value.  I gave up on that futile quest in the 1900s.  I understand
> that Java is described by its creators as call-by-value, and I understand
> what they mean by that.  I've never taken a compsci course that mentioned
> Java (it was invented long after my college days), but 5 minutes' googling
> turned up many sets of university lecture notes on Java.  Google's top hit
> of that sort is here:
> 
>     www.d.umn.edu/~rmaclin/cs5641/Notes/Lecture13.pdf
> 
> It claims that "everything is call by value" in Java, then trips over itself
> with the convoluted "but for objects it's really a copy of a reference"
> business, and ends with the incorrect claim that "but the pointer to the
> object can then also be changed".
> 
> The second hit was:
> 
>     http://www.cs.wright.edu/people/faculty/rrea/jmethod.html
> 
> This one claims:
> 
>     Java provides both a Call by Value and a Call by Reference capability.
>     Call by Value means that the value is passed to the method and any
>     changes to that passed value will NOT change the value in the calling
>     method.  Any primative [sic] variable that is passed such an [sic] an
>     integer, a float, and so on is automatically passed by value.  This
>     is a safe, but limiting way to pass a parameter.
> 
>     Call by Reference means that the called method can change the value
>     in the calling method. If you pass an Object, it is automatically
>     passed by reference.  We will get to objects in the next chapter.
> 
> I know what he means too, and expect that his students will come out with a
> clearer understanding of how Java actually works.
> 
> The third:
> 
>     http://www.sor.princeton.edu/~rvdb/201/lectures/lec22.pdf
> 
> says Java is call-by-value but then does the usual dance exempting objects
> from the implications of that, and goes on to say that C/C++ structs are
> "the real thing" when it comes to call-by-value.  So I suspect that
> Professor Vanderbei would be happier if he didn't feel compelled to say that
> everything in Java is call-by-value.

Well, my first reference for Java is what James Gosling has to say:

    All parameters to Java methods are "pass by value."

("The Java(TM) Programming Language", 2nd ed., by Arnold and Gosling)

Several pages earlier in the book he writes that all objects in Java
are accessed by object references and one page later he explains that
object references are also passed by value.  This isn't too
complicated -- the entire explanation is 7 or 8 sentences total.
Although I'm sure Gosling knows that this is an area that confuses
many, he keeps it simple perhaps in the belief that programmers will
find it clear once they have used Java for a while.  Of course this
thread seems to cast doubt on that hope.  It is clear, however, that
Gosling understands Java a lot better than the lecturers in the
references you found.  I fervently hope that they use texts that are
more clear than their muddled lecture notes.

It's unfortunate that many people find Java argument passing difficult
to explain.  This is partly Java's fault. Since primitive types like
boolean, char and the numeric types aren't Java objects, Java r-values
are messier than they should be.

The other reason this trips up so many people is that they don't have
a precise idea what call-by-value is and they don't have a firm grasp
of what values are.  It's very very simple but also slightly subtle,
and this is exactly the kind of quicksand that traps the unwary.  An
imprecise idea of what c-b-v is leads to assumptions that c-b-v
implies consequences that are not correct.  If people started with the
correct definition of c-b-v rather than a vague notion and understood
the nature of values in the programming language they were considering
then most of this difficulty would go away.

I do agree that if someone (even a professor) has trouble explaining
why Java is call-by-value that it would be better if he or she didn't
try.

> And on it goes, seesawing between lecturers who stick to the
> everything-is-call-by-value-in-Java party line then scrambling to explain
> that object arguments in Java aren't really copied, and those who ignore the
> party line, sticking to the more traditional distinction that primitive
> types in Java are passed by value but that class objects and arrays are
> passed by reference.

There's the problem.  "... object arguments in Java aren't really
copied ..."  Neither the Dragon Book definition or Sethi's definition
say anything about copying.  Once you've made this error, you're
starting down the wrong path and your chances of recovery are slim.
Assuming that copying is part of the definition of call-by-value tends
to lead to all kinds of incorrect conclusions such as the ones
evidenced in the CLU manual.

Argument passing methods like call-by-value, call-by-reference and
call-by-name tell you what is passed (r-value, l-value and name,
respectively), not what is done with the with what is passed (copied,
folded, mutilated or spindled).  Copying isn't part of the definitions
of any of them.  There is an argument passing method used in Ada that
is defined by copying.  (Ironically enough it doesn't have a standard
name, but value-result or copy-in-out are commonly used).  Mercifully
we don't have to try to explain that here.

I don't think you expect that an assignment statement will copy
values, so it would be better to not add that confusion to parameter
passing either.  If you can explain what gets copied by x = y then you
have explained what gets copied by f(y).  I am very interested to hear
why you think these are different in Python.  The example you used
before showed how they are exactly the same.  (This is precisely what
call-by-value requires.)

> I do believe they would benefit by dropping that whole mess and embracing
> call-by-object terminology instead.  Short of that, I don't care what they
> want to say call-by-value means.

I completely agree that anyone who can't coherently explain
call-by-value and how it applies to Pascal, C, Java and Python should
not use the term.  There are other words that can be used for
explanation.  It is possible to have a perfect understanding of a
language like Python but still have an imperfect understanding of
call-by-value.

> > I didn't just invent a definition for call-by-value based on what I
> > thought it should mean.  I learned the definition at school from
> > professor Fischer.  Professor Fischer researches computer programming
> > languages, and I presume that he taught his class the definition of
> > the term as it is used in the field.
> 
> I'm sure he taught the definition the subculture he belongs to uses most
> often.  Fine by me.  Believing that's the only definition in wide use
> remains silly, though.  You're also free to believe that only people who
> embrace Prof. Fischer's definition in all respects are expert practitioners,
> but the world you live in shrinks with each such demand <0.5 wink>.  I have
> a bold suggestion:  ask Prof. Fischer whether *he* believes there's only one
> true defintion of this term (BTW, is this Charles Fischer of UW Madison?  if
> so, I'd say his specialty is more compiler design than language design; and
> if you can, get him to argue about this with Baker on some Lisp list
> <wink>).

Yes, it was Charles Fischer.  You are correct -- his specialty is
implementation of programming languages.  In the past his research
centered around error correction in parsers and I think some work with
attribute grammars.  I'm at least 15 years out of date on his work so
I don't know anything about his recent research.  In addition to a
rather rigorous graduate seminar on compiler construction he sometimes
teaches undergrad CS courses like introductory programming language
theory.  Actually I should just check to see what his compiler book
says about call-by-value, but I don't own a copy.  (It was first
published a few years after I was a student.)

There's no real need to argue with Baker since he actually agrees with
the Dragon Book on the definition of call-by-value.  He doesn't like
that canonical definition, but the whole point of his paper is that he
acknowledges that it is the current definition.  Baker wants to change
it.  I think it's fine the way it is.

> > Here is what Ravi Sethi says in "Programming Languages: Concepts and
> > Constructs": ...
> 
> Sorry, I'm spacing out on this, and am out of time for this anyway.  You
> don't need to convince me that many people in the field embrace the
> definition you like -- I knew that going into this.  It doesn't change that
> it's not universally accepted, or that I strongly doubt that any of the
> authorities you've been exposed to would claim that this is a matter of One
> Objective Truth.  Indeed, the dueling quotes game is proof that it isn't.
> Unlike biologists arguing over the giant panda, we're not studying physical
> reality here -- outside the mathematical foundations of compsci, words are
> chosen mostly for perceived utility, and those perceptions vary (for good
> reasons) across groups.

Well, I'm not aware of much debate over the meaning of call-by-value
in the programming language theory community.  There certainly is a
lot of confusion among the amateurs.  It comes up not just in Python,
but commonly in Java and Lisp too.  Avoiding the term is possible and
probably the best solution in many cases.  I support anyone's decision
to use a different term if they think that it will aid in
understanding or teaching.

At the same time I also think it's important to fight for the clear
and correct definition of the term.  Persons new to studying computer
programming language theory should be exposed to the correct
definition of basic terms like call-by-value.  It might even help
someone get a question right on an undergrad CS exam.

> > I have no objection to calling Python argument passing
> > call-by-object-reference.  (I would be curious to know why you think
> > that is different from call-by-value in a language like Python where
> > r-values are object references.)
> 
> I'm not answering this one again:  it's a matter of what's *helpful*, as
> I've explained several times already.  The mess I found in the Java lecture
> notes is typical of what comes from *not* breaking away from these old
> overloaded phrases.  At heart, saying "call by object" invites exploration,
> while saying "call by value" invites confusion.  Nobody except a computer
> newbie comes to Python without *some* idea of what "call by value" means,
> and they really don't have the same ideas.  Tedious lectures opposing fixed
> ideas are far less productive than approaching it from an angle they don't
> have preconceived notions about.

I suspect that this is as close as we'll get.  Your point is that many
terms and in particular call-by-value don't have a single fixed
meaning.  Depending on the definition chosen, a particular language
could be classified as c-b-v or not.  If the accepted meanings really
diverge this much then the term is worthless.  I am not willing to
discard it.  It is abundantly clear that people have many different
ideas about what c-b-v means, but I think there is a commonly accepted
definition in the expert programming language implementation and
theory community, that that definition is isomorphic to the one given
in the Dragon Book and that that definition applies unambiguously in
cases like Python.  The mistaken notions about c-b-v would require
that Pascal and C not be considered c-b-v.  I think this would be an
absurd result and I reject it.

You have said that there's no rational way that Python can be
considered call-by-value.  I simply don't agree.  I think the simplest
definition of call-by-value (it's only one sentence long!) makes
Python a call-by-value language.  People who try to read far too much
into call-by-value are the ones that get confused.  (They always get
hung up on mutability.  The passed object can be mutated, but it will
always be the same object after the call that it was before the call.)
We probably won't get any closer on that point either.

I am in full agreement with you that the term call-by-value causes a
lot of confusion.  I also agree that it isn't important to describe
Python's parameter passing as c-b-v when teaching or discussing the
language outside of a programming language theory environment.  It
would be a mistake to call Python call-by-reference, but other terms
like call-by-sharing or call-by-object-reference are fine.  Of course
then you should say Python assignment is assign-by-sharing or
assign-by-object-reference too, since assignment uses exactly the same
values as parameter passing does in a call-by-value language such as
Python....




More information about the Python-list mailing list