why cannot assign to function call

Mark Wooding mdw at distorted.org.uk
Tue Jan 13 18:06:58 EST 2009


Steven D'Aprano <steven at REMOVE.THIS.cybersource.com.au> wrote:

> I found it interesting. 

Well, that's something, at any rate.

> I think this conversation is reaching it's natural end. Frustration
> levels are rising. 

I think you may be right.  That said...

> So I'm going to take a different tack in an attempt to reduce
> frustration levels: if I can't convince the other side they're wrong,
> can I at least get them to understand where I'm coming from a little
> better?

Maybe...

> As I see it, this conversation is floundering on two radically different 
> ideas about what it means to say a language uses pass-by-foo.

You might be right, but I'm unconvinced.

> On the one hand, some people (me and possibly rurpy) consider "this is 
> pass-by-foo" to be a statement about behaviour directly visible to the 
> programmer. We have a set of behavioral traits in mind, and if a language 
> exhibits those behaviours, then it is clearly and obviously pass-by-foo 
> no matter how that behaviour is implemented. I'll call these the 
> behaviorists.

Here's the problem.  I think I'm in that camp too!

I'm going to move away from the formal semantics stuff and try a
different tack.  Here's what I think is the defining property of
pass-by-value (distilled from the formal approach I described earlier,
but shorn of the symbolism):

  The callee's parameters are /new variables/, initialized /as if by
  assignment/ from the values of caller's argument expressions.

I'd just like to examine that for a bit.  Firstly, let's expand it from
the soundbite: basically what it says is that you should be able to
replace

  function mumble(a, b, c) { stuff in terms of a, b, and c }
  ...
  mumble(1 + 2, xyz, whatever)

with

  ...
  fresh_a = 1 + 2
  fresh_b = xyz
  fresh_c = whatever
  stuff in terms of fresh_a, fresh_b, and fresh_c

with no observable difference (here, fresh_a and so on are a variable
names not appearing in the rest of the program).  So:

  * It captures C's behaviour (at least if you don't count arrays --
    let's not open that one again), and Pascal's normal behaviour.
    Assigning to the parameters doesn't affect the caller's argument
    variables because the parameters are fresh variables.

  * It /doesn't/ capture Fortran's behaviour, or Pascal's `var'
    parameters, because obviously assignment to parameters in Fortran
    /can/ affect the caller's argument variables

  * It also doesn't capture exotic things like Algol's call by name, and
    lazy evaluation, because there's an evaluation step in there.

My soundbite definition for pass-by-reference is this:

  The callee's parameters are merely /new names/ for the caller's
  argument variables -- as far as that makes sense.

There's a caveat there for argument expressions which don't correspond
directly to variables -- and I've glossed over the issue of lvalue
expressions which designate locations and all of that.  The idea is that
you can replace

  function mumble(a, b) { stuff in terms of a and b }
  ...
  mumble(xyz, whatever)

by

  ...
  stuff in terms of xyz and whatever

This does indeed capture Fortran, and Pascal's `var', while excluding C
and Pascal non-`var'.  Good!

So... obviously I'm going to claim that Python is pass-by-value.  Why?
Because its argument passing works the same way as its assignment.

But! (you claim) ...

> Python simply can't be pass-by-value, because it doesn't behave like
> pass-by-value in other languages (particularly C and Pascal).

Ah! (say I) but assignment in C and Pascal looks different from the way
it looks in C -- and in exactly the same way that argument passing looks
different.  And there, I think, I'm going to rest my case.

I'm sorry I took so long to distill these thoughts.  Thank you for
putting up with my theoretical meanderings on the way.

-- [mdw]



More information about the Python-list mailing list