Re: why cannot assign to function call
In article mailman.952.1235850376.11746.python-l...@python.org, rdmur...@bitdance.com wrote: I think this is the key point. I am an experienced Python programmer, and I had to think long and hard about what you were saying, Mark, in order to understand how it applied to Python. I think this means that your model and/or your level of abstraction is not natural when trying to understand Python programs, and I think I may have figured out why. Good job! I'll just add one little bit to your excellent rebuttal: Assignment in Python changes a mapping (mutates a namespace object). In this case the mapping being changed is the local function's namespace mapping. We can't affect the caller's namespace mapping, because (in this function) we don't have access to the caller's namespace object. So I would diagram it like this: y (in global env.) - 'virgin' x (in function 'test') 'clobbered' Although you later make clear that this model works well with lists, too, I personally find it easier to separate the concept of name (which is strictly the set of legal Python identifiers) with the more general concept of target. Consider this: L = [1, 2, 3] L[1:1] = ['foo', 'bar'] What do you call ``L[1:1]``? I call that a target (and in fact, so do the Python docs). -- Aahz (a...@pythoncraft.com) * http://www.pythoncraft.com/ All problems in computer science can be solved by another level of indirection. --Butler Lampson -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Aahz wrote: In article mailman.952.1235850376.11746.python-l...@python.org, rdmur...@bitdance.com wrote: I think this is the key point. I am an experienced Python programmer, and I had to think long and hard about what you were saying, Mark, in order to understand how it applied to Python. I think this means that your model and/or your level of abstraction is not natural when trying to understand Python programs, and I think I may have figured out why. Good job! I'll just add one little bit to your excellent rebuttal: Assignment in Python changes a mapping (mutates a namespace object). In this case the mapping being changed is the local function's namespace mapping. We can't affect the caller's namespace mapping, because (in this function) we don't have access to the caller's namespace object. So I would diagram it like this: y (in global env.) - 'virgin' x (in function 'test') 'clobbered' Although you later make clear that this model works well with lists, too, I personally find it easier to separate the concept of name (which is strictly the set of legal Python identifiers) with the more general concept of target. Consider this: L = [1, 2, 3] L[1:1] = ['foo', 'bar'] What do you call ``L[1:1]``? I call that a collection slot. I call that a target (and in fact, so do the Python docs). Targets encompass both names and slots. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Ethan Furman et...@stoneleaf.us writes: Mark Wooding wrote: Here's what I think is the defining property of pass-by-value [...]: The callee's parameters are /new variables/, initialized /as if by assignment/ from the values of caller's argument expressions. 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. Greetings, Mark! You posted to the whole group -- probably using a wide-reply option while reading the mailing list. Still, I'll give an answer here in order to help any other readers of the list or newsgroup. However, as far as I'm concerned, this discussion is basically at an end and I'm not really interested in starting it up all over again. To that end, I've set followups to `poster'. I shall continue reply to private email which seems interested in a sensible discussion. I was hoping you might be able to clarify those last two sound bites for me -- I think I understand what you are saying, but I'm confused about how they relate to Python... Specifically, how is a new name (pbr) different, in Python, from a new name initialized as if by assignment (pbv)? It seems to me than you end up with the same thing in either case (in Python, at least), making the distinction non-existent. You've missed a level of indirection. In particular, `names' aren't things that you initialize. They're things that you /bind/ to variables. The crucial difference is that, in pass-by-value, new variables are created as an intrinsic part of the process, whereas in pass-by-reference, new variables are not (usually) created, and instead the formal parameter names are bound to the caller's pre-existing argument variables. It's worth noting that I use the terms `name' and `binding' in different ways from most of the Python community. This is unfortunate. The discrepancy is actually because the Python meanings of these words are not the same as the meanings in the wider computer science and mathematical communities. For example, many Python users seem to use `binding' to mean `assignment', which is a shame because it leaves the concept that is usually called `binding' without a name. So I'll stick with the wider meanings. A while ago, I posted an essay -- to this group -- which may help explain the concepts: Message-ID: 8763k14nc6.fsf@metalzone.distorted.org.uk http://groups.google.com/group/comp.lang.python/msg/f6f1a321f819d02b On with the question. def func(bar): bar.pop() Pass-by-reference: foo = ['Ethan','Furman'] func(foo) # bar = foo Pass-by-value: foo = ['Python','Rocks!'] func(foo) # bar is new name for foo # is this any different from above? If I have this right, in both cases foo will be reduced to a single-item list after func. You're correct. So: we can conclude that the above test is not sufficient to distinguish the two cases. Any further explanation you care to provide will be greatly appreciated! This test is sufficient to distinguish: def test(x): x = 'clobbered' y = 'virgin' test(y) print y If it prints `virgin' then you have call-by-value. If it prints `clobbered' then you have call-by-reference. Let's examine the two cases, as I did in the essay I cited above. I'll do call-by-value first. First, we define a function `test'. Then, we initialize `y'. It's worth examining this process in detail. The name `y' is initially unbound. so it is implicitly bound to a fresh variable. Then, (a reference to) the string object 'virgin' is stored in this variable. We can show this diagrammatically as follows. y (in global env.) [VAR] --- 'virgin' (In the diagrams, === denotes a binding relationship, between names and variables; and --- denotes a reference relationship, between variables and values.) Next, we call the `test' function. Call-by-value says that we must evaluate the argument expressions. There's only one: `x'. The value of a name is obtained by (a) finding which variable is bound to the name, and (b) extracting the value from this variable. Well, the variable is the one we just bound, and the value stored is (the reference to) the string 'virgin'. So the result of evaluating the argument expressions is simply (the reference to) that string. The function has one parameter, `y'. A new environment is constructed by extending the global environment. In this new environment, the name `y' is bound to a fresh variable -- distinct from all others, and especially from the variable bound to `x' -- and in that variable we store the value of the corresponding argument expression. Result: the function body is executed in an environment which is like the global environment except that `y' is bound to a fresh variable containing 'virgin'. y (in global env.)
Re: why cannot assign to function call
Mark Wooding m...@distorted.org.uk wrote: Ethan Furman et...@stoneleaf.us writes: Mark Wooding wrote: Here's what I think is the defining property of pass-by-value [...]: The callee's parameters are /new variables/, initialized /as if by assignment/ from the values of caller's argument expressions. 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. Greetings, Mark! You posted to the whole group -- probably using a wide-reply option while reading the mailing list. Still, I'll give an answer here in order to help any other readers of the list or newsgroup. However, as far as I'm concerned, this discussion is basically at an end and I'm not really interested in starting it up all over again. To that end, I've set followups to `poster'. I shall continue reply to private email which seems interested in a sensible discussion. I just popped in to this thread, and read the whole thing in a marathon session (why, I'm not quite sure, but somehow I found it interesting :). I'm going to follow up here at the risk of annoying Mark, because I think there may be a way to reconcile the language in a way that is helpful in explaining things to Python beginners. I was hoping you might be able to clarify those last two sound bites for me -- I think I understand what you are saying, but I'm confused about how they relate to Python... I think this is the key point. I am an experienced Python programmer, and I had to think long and hard about what you were saying, Mark, in order to understand how it applied to Python. I think this means that your model and/or your level of abstraction is not natural when trying to understand Python programs, and I think I may have figured out why. Specifically, how is a new name (pbr) different, in Python, from a new name initialized as if by assignment (pbv)? It seems to me than you end up with the same thing in either case (in Python, at least), making the distinction non-existent. You've missed a level of indirection. In particular, `names' aren't things that you initialize. They're things that you /bind/ to variables. The crucial difference is that, in pass-by-value, new variables are created as an intrinsic part of the process, whereas in pass-by-reference, new variables are not (usually) created, and instead the formal parameter names are bound to the caller's pre-existing argument variables. I think that the reason Ethan missed that level of indirection is that Python hides that level of indirection from the programmer. And I think that this is where the conversation between you and Steven got hung up, Mark. You are firmly grounded in how languages work in general, so your conceptual model naturally includes details that Steven's conceptual model can ignore, because he's dealing only with the model used by Python. He can (and wants to!) ignore the level of indirection that Python doesn't even give him access to. In short, there is a reason why you almost never hear the word 'variable' from an experienced Python programmer when talking about the Python model. That level detail is abstracted into something else by the Python conceptual model: it becomes a namespace mapping names to objects. It's worth noting that I use the terms `name' and `binding' in different ways from most of the Python community. This is unfortunate. The discrepancy is actually because the Python meanings of these words are not the same as the meanings in the wider computer science and mathematical communities. For example, many Python users seem to use `binding' to mean `assignment', which is a shame because it leaves the concept that is usually called `binding' without a name. So I'll stick with the wider meanings. It seems to me from reading the thread that everyone is actually in pretty good agreement that 'name' is the symbol one types in the program source to denote...something. It's the something where things get tricky. So let's leave name itself alone. But let's replace 'assignment' and 'binding' with the concept that is more natural to Python's model, that of a mapping. A namespace maps names to objects. If I'm understanding you correctly, Mark, in your model what I'm calling a namespace is the set of variable bindings in a particular environment. Because Python does not allow the programmer to actually rebind a variable (Python handles the association between name and variable completely behind the scenes), Python programmers naturally skip past that name-to-variable binding step, and conceptually think about the name being bound to the _value_ instead. After all, that's the only thing they can change. This may be unfortunate from a precisionist viewpoint, but it is both natural and, IMO, inevitable, because it makes thinking about Python programs more efficient. A while ago, I
Re: why cannot assign to function call
Ethan Furman et...@stoneleaf.us wrote: Specifically, how is a new name (pbr) different, in Python, from a new name initialized as if by assignment (pbv)? It seems to me than you end up with the same thing in either case (in Python, at least), making the distinction non-existent. def func(bar): bar.pop() Pass-by-reference: foo = ['Ethan','Furman'] func(foo) # bar = foo The problem here, in my view, is the terminology. It is true that, inside the function func, bar will refer to the same list that foo refers to. However, it is inaccurate to think of this list as foo. There exists a list -- an anonymous list in object space -- and while func is executing, there are two names bound to that list. So, the bar.pop() instruction changes that anonymous list, you'll see those changes if you inspect foo after the call. Pass-by-value: foo = ['Python','Rocks!'] func(foo) # bar is new name for foo # is this any different from above? Yes, it's different. If Python really had pass by value, that function call would pass a COPY of the list. The function would receive the list's value, not the list itself. If I have this right, in both cases foo will be reduced to a single-item list after func. Any further explanation you care to provide will be greatly appreciated! Nope. Under pass-by-value semantics, func could dance on the list to its heart's content, and foo would not be changed. -- Tim Roberts, t...@probo.com Providenza Boekelheide, Inc. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Mark Wooding wrote: Steven D'Aprano ste...@remove.this.cybersource.com.au wrote: 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. 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. Greetings, Mark! A little background on myself, hopefully making my soon-to-follow question less idiotic. I've been in the PC world for 20+ years now, and dabbled with programming through much of that time. I've written small utilities in Assembly (x86) and CL (AS/400), I just recently wrote a dbf module for python, and I've studied (in and out of the classroom) C, Fortran, Pascal, Java, Perl, Php, and Basic. While I have had some formal training, my degree is in Business (long story -- I would rather have done CS), and much of your explanation in previous posts was way over my head. I am in complete agreement with Steven's description of the behaviorist point of view for pass-by-value and pass-by-reference, and if asked I would say Python is pass-by-object. I offer this so you know where I'm coming from, not from any hostile motive. I was hoping you might be able to clarify those last two sound bites for me -- I think I understand what you are saying, but I'm confused about how they relate to Python... Specifically, how is a new name (pbr) different, in Python, from a new name initialized as if by assignment (pbv)? It seems to me than you end up with the same thing in either case (in Python, at least), making the distinction non-existent. def func(bar): bar.pop() Pass-by-reference: foo = ['Ethan','Furman'] func(foo) # bar = foo Pass-by-value: foo = ['Python','Rocks!'] func(foo) # bar is new name for foo # is this any different from above? If I have this right, in both cases foo will be reduced to a single-item list after func. Any further explanation you care to provide will be greatly appreciated! ~Ethan~ -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Fri, 27 Feb 2009 23:55:43 -, Ethan Furman et...@stoneleaf.us wrote: I'm not Mark, but you did post to the whole group! [snippety snip] Specifically, how is a new name (pbr) different, in Python, from a new name initialized as if by assignment (pbv)? It seems to me than you end up with the same thing in either case (in Python, at least), making the distinction non-existent. def func(bar): bar.pop() Pass-by-reference: foo = ['Ethan','Furman'] func(foo)# bar = foo Pass-by-value: foo = ['Python','Rocks!'] func(foo)# bar is new name for foo With true pass-by-value, this comment is not true. Bar would be a copy of foo, not a new name. What happens to bar is then not reflected in foo (depending on how deep the copy is), which is the objective of pass-by-value. -- Rhodri James *-* Wildebeeste Herder to the Masses -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Jan 5, 9:03 am, Derek Martin c...@pizzashack.org wrote: On Sat, Jan 03, 2009 at 10:15:51AM +, Marc 'BlackJack' Rintsch wrote: On Fri, 02 Jan 2009 04:39:15 -0600, Derek Martin wrote: On Tue, Dec 30, 2008 at 02:21:29PM +, John O'Hagan wrote: What the Python community often overlooks, when this discussion again rears its ugly head (as it seems to every other hour or so), is that its assignment model is BIZARRE, as in it's conceptually different from virtually all other languages substantially taught in undergraduate computer science programs. What's the difference between Python and Java or C# here!? Or are they also BIZARRE!? I am happily ignorant of C#. As for Java, take the following code: a = 6; a = 5; In Python, when you execute the equivalent code, it causes two different objects to spring into existence, the first of which may be cleaned up by the GC (except that since we're using small integers, that's not likely to happen). Unless I'm misinformed (which is very possible, my experience with Java has been extremely limited) in Java that's not the case... the storage is allocated to the name a when you execute its declaration, and the *same storage* is reused upon subsequent assignment. That behaves exactly like named bins. And for that matter, it's pretty unintuitive generally. Names and objects are quite natural IMHO. There are many real world objects which we attach one or more names to, or refer to in sequences like please give me the third book on that shelve (``shelve[2]``). Indeed, but the way we assign names to them does not behave as it does in Python. Nor does Python's assignment work like it does in algebra, or anywhere else the Python student is particularly likely to have seen variable assignment before encountering it in Python. Let's define intuitive, shall we? From dictionary.com (choosing the definition which most easily makes my point): intuitive: adj. capable of being perceived or known by intuition. I'm going to go out on a limb and assert that there's NO POSSIBLE WAY a student could intuit Python's variable assignment behavior, having never been exposed to that same behavior prior. It needs to be taught. Which limb would you like to go with? That is, in what I'll call normal computer languages, a variable name is thought of as the address of a bin where some data is stored, and the name is inexorably tied to that bin. You just call that normal or intuitive because that's what you learned first. In a sense, yes... but isn't that what intuition really is? You can figure something out whithout being told how it works... That's either because it's immediately obvious from observing it, or it behaves like something you've seen before. That is what intitive is. No. You've just said it yourself, something is intuitive if it behaves like something you've seen before. People have different experience, people deeply involved in mathematics would see named bin is the obvious thing to go, however commoners (including mathematician) will have different say: Derek is the name of a human object instead of Derek is a specific collection of atoms that resembles human. This is also why python community tend to avoid the use of variable, because python's name doesn't behave like variables in mathematics, it behaves like name. (heck, one would argue that even variable in programming is not exactly the same as variable in mathematic either) I think the bin model is more complex because you don't just have a name and an object but always that indirection of the bin. I cheerfully disagree. :) Named bins is essentially how algebra works, and how several generations of computer languages, not to mention the actual machine language those generated, behaved, before the current crop. Those interpretations came first, because, much as in the evolution of any other science, that was the model which was most intuitive or easily explained. No, it is neither the most intuitive nor easily explained, but indeed it is the simplest to implement as humans at that time have limited capability to design chips. In the early days, ease of use is not a priority, simplicity is. But you need not take my word for it. Simply read the archives and see for yourself how much confusion this has caused on this list. [Please include the closely related behavior of parameter passing in your search.] The first time I used python, I didn't have any idea how the object model works, and I don't have experience in similar programming concept nor was python my first language, yet I have no difficulty in understanding why things goes like it is. For me, at least, python's object model is intuitive (do you remember about the limb?). -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Tue, 13 Jan 2009 23:06:58 +, Mark Wooding wrote: 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. Well, that's one way of looking at it, I guess. The problem is that when I do this: x = something # just an example, not necessarily a string y = x Does the line y=x create a new variable? Depends on what you mean by variable. If you mean a new name, symbol or whatever, then obviously yes. But if you mean a new value, namely the string something, then not necessarily. In Pascal (and C?) those bytes are copied, giving two independent values which just happen to be equal. In Python, both x and y refer to the same string, and I choose the term deliberately. See, I'm not *entirely* against the use of referring to references, when appropriate. I think we just differ on when we think it's appropriate. Which I guess brings us back to your earlier assertion (I think it was you) that people aren't confused about argument passing, they're confused about 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 I'm sorry, that confuses me. Assignment in C looks different from the way it looks in C? I guess the second C should be Python. -- 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. And thank you for not kill-filing me when tempers were getting short. -- Steven -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Steven D'Aprano ste...@remove.this.cybersource.com.au wrote: Ah! (say I) but assignment in C and Pascal looks different from the way it looks in C I'm sorry, that confuses me. Assignment in C looks different from the way it looks in C? I guess the second C should be Python. Yes, you're right. Stupid mistake on my part. -- [mdw] -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Aaron Brady castiro...@gmail.com wrote: On Jan 13, 5:06 pm, Mark Wooding m...@distorted.org.uk wrote: snip 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. In other words, the same as assignment in that language. It's the same as assignment of /values/ to /fresh variables/. It's important to specify that argument expressions are fully evaluated, and that nothing gets overwritten in the process. So the difference between Python and C is that C copies values during assignment, but Python doesn't. Therefore the argument passing works the same way. Because its argument passing works the same way as its assignment. That would be called pass-by-assignment. But you've just postponed explaining yourself. Stop delegating and work. Huh? I'm really at a loss to know what you want me to do. I can't help it if people are confused over the phrase `pass-by-value', beyond trying to explain what it means using clear definitions. The `value' part means that argument expressions are /evaluated/ (turned into values), which forces applicative-order semantics rather than (say) normal-order. -- [mdw] -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Jan 14, 3:51 am, Mark Wooding m...@distorted.org.uk wrote: Aaron Brady castiro...@gmail.com wrote: On Jan 13, 5:06 pm, Mark Wooding m...@distorted.org.uk wrote: snip 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. In other words, the same as assignment in that language. It's the same as assignment of /values/ to /fresh variables/. It's important to specify that argument expressions are fully evaluated, and that nothing gets overwritten in the process. So the difference between Python and C is that C copies values during assignment, but Python doesn't. Therefore the argument passing works the same way. That interferes with Joe's claim that the value of a variable is a memory address. I think you'd have better luck sticking with pass-by- assignment. A Java reference and a C++ reference are different things, somewhat regrettably. Because its argument passing works the same way as its assignment. That would be called pass-by-assignment. But you've just postponed explaining yourself. Stop delegating and work. Huh? I'm really at a loss to know what you want me to do. You were remarkably civil about that. I regretted it as soon as I sent it. You keep wanting to say but it's just a reference, it's just a reference. But all that does is put more burden on the fellow that explains what a reference is. It took me years to grasp pointers... possibly all the way up until comp. architecture class. (As such, I find you're shirking.) I can't help it if people are confused over the phrase `pass-by-value', beyond trying to explain what it means using clear definitions. The `value' part means that argument expressions are /evaluated/ (turned into values), which forces applicative-order semantics rather than (say) normal-order. I can't believe it's taken this long, including October's discussion (name dropping and especially with my dual (not duel, ha!) in Philosophy /dropping), but I'd finally like to quote the manuals. An object's identity never changes once it has been created. The value of some objects can change. - http://docs.python.org/reference/datamodel.html Therefore, an object's value is not its identity. (Its identity is something other than its value.) You might get away with 'pass-by- identity'. After 'X= [1, 2, 3]', X refers to an object. You can change two things: (1) the fact that X refers to that object, (2) that object. In 'X= None' and 'del X', you change the fact that X refers to that object. In 'X[:]= [4, 5, 6]' and 'X.pop()', you change the object. Explanatory note: It really takes two sentences to say that. 'X refers to an object. You can change the object, or change the fact that X refers to it.' If you try to say it in one sentence, the distinction collapses: 'You can change (1) the object that X refers to, or (2) the object that X refers to.' Irony! 'X' is a key in a namespace. namespace[ 'X' ]= [1, 2, 3] namespace[ 'Y' ]= namespace[ 'X' ] del namespace[ 'X' ] No matter how hard you try, if you don't have both of two things, 'namespace' and 'X', you can't change namespace[ 'X' ]. And back to the OP (long since gone, naturally): You can do: f()[:]= [1, 2, 3] As well as: f().replace( [1, 2, 3] ) As well as: f().contents= [1, 2, 3] The particular thing s/he wanted isn't one of these. Lastly, I think it would do you and Joe good to ignore some details, when thinking about beginners. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Sat, 10 Jan 2009 15:46:35 +, Mark Wooding wrote: [Another tome. I hope this contains sufficient new material to continue to be of interest to other readers.] I found it interesting. I don't know about others. However, at 756 lines (including quoting) it was nearly double the size of my previous tome, so I think this discussion is clearly not going anyway. I think this conversation is reaching it's natural end. Frustration levels are rising. I could -- and in fact intended to -- write a point-by-point argument, but that would only send the size of this skyrocketing and probably nobody would read it. 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? 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. 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. On the other hand, others (I'm going to put Joe and Mark into this group) consider pass-by-foo to be a statement about mechanism, or if you prefer, implementation. If a compiler does X, then the language is pass- by-foo, regardless of behaviour. I'll call these the mechanists. To a behaviorist, Python simply can't be pass-by-value, because it doesn't behave like pass-by-value in other languages (particularly C and Pascal). The Python community has a tradition of duck-typing: if it quacks like a duck and swims like a duck, then it is a duck in every way that is important. If it can't swim and doesn't quack, it isn't a duck, no matter how much duck DNA is in it. If it flies, that's irrelevant to the question, because some ducks fly and some don't. According to the behaviorist view, what makes an evaluation strategy call- by-reference is whether or not it exhibits the following three behaviours: (1) passing a value to a function does not copy the value; (2) modifications to the value inside the function are visible to the caller; (3) assignments to the value inside the function are visible to the caller. where value means the thing the programmer manipulates symbolically in source code. Values are ints, strings, lists and so forth. Pointers or references are *only* values if the language allows you to write the equivalent of: ptr = SomeReferenceTo(x) # like Pascal's ptr := ^x; x = Dereference(ptr) # like Pascal's x := ptr^; According to this viewpoint, Python clearly cannot be pass-by-reference because (3) is not true. Similarly, what makes pass-by-value is: (1) passing a value to a function makes a copy of the value, where value is an entity the programmer can symbolically manipulate in source code (lists, ints, floats etc.); (2) modifications to the value inside the function are not visible to the caller; (3) assignments to the value inside the function are not visible to the caller. According to this viewpoint, Python clearly cannot be pass-by-value either because neither (1) nor (2) are true. The underlying mechanism is irrelevant. Mechanists take a different view. A typical argument paraphrased from Joe in previous threads is: Of course Python makes a copy of the value you pass to a function. The difference is that the value you pass is a reference. Mechanists reject the restriction that values only include entities manipulated by the programmer. Consequently they're prepared to say that values in Python aren't the things which Python programmers symbolically manipulate (strings, ints etc.) but are references. Needless to say this claim strikes behaviorists as nonsensical and rather incoherent. If the value of 2 isn't 2, then value has no meaning. If mechanists see the behaviorists as willfully ignorant, the behaviorists see the mechanists as being blatantly obfuscatory, introducing irrelevant details and ignoring clear examples of the duck quacking. And these two viewpoints are why this debate never ends, merely goes quite for a few weeks or months. -- Steven -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Steven D'Aprano ste...@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] -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Jan 13, 5:06 pm, Mark Wooding m...@distorted.org.uk wrote: snip 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. In other words, the same as assignment in that language. snip Because its argument passing works the same way as its assignment. That would be called pass-by-assignment. But you've just postponed explaining yourself. Stop delegating and work. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
ru...@yahoo.com ru...@yahoo.com wrote: Mark Wooding wrote: ru...@yahoo.com ru...@yahoo.com wrote: What is the observable difference between converting an array to a reference (pointer) to that array and passing the reference by value, and passing the array by reference? For one: #include stdio.h static size_t foo(char v[]) { return sizeof v; } int main(void) { char v[sizeof(char *) * 10]; puts(foo(v) == sizeof(char *) ? pointer : reference); return (0); } You are saying that because the size of the argument (10) is not available in the function, it cannot be call-by-reference? No, I'm saying that because the parameter has pointer type, not array type, it's a pointer being passed by value, not an array being passed by reference. And the size of the argument is not 10: it's 10 times as much as the (nonzero) size of a pointer -- and therefore distinguishable on any C implementation. I think fortran is accepted as the archetypal call-by- reference language and it does not automatically supply argument size information to functions. Well and dandy; but Fortran doesn't turn arrays into pointers. For another: static void bar(char v[]) { char ch = 0; v = ch; } /* type error if arrays truly passed by reference */ v can be used as an array reference, e.g. v[1] = 23 exactly as in the pass-by-reference fortran example. And v can also be used as a local variable and reassigned. If the first option was the only one, would you not say C was definitely pass-by-reference (for arrays)? If one could only use v as a pointer (i.e. access the argument array as *(v+1) = 23, then I would say that arrays are not passed at all, only pointers by-value. Your knowledge of C is clearly limited, at best. The notations *(p + i) and p[i] are entirely equivalent in every way, since the square-brackets indexing operator is in fact defined in terms of pointer dereference (6.5.2.1): : [#2] 2 A postfix expression followed by an expression in square : brackets [] is a subscripted designation of an element of an array : object. The definition of the subscript operator [] is that E1[E2] is : identical to (*((E1)+(E2))). Because of the conversion rules that : apply to the binary + operator, if E1 is an array object : (equivalently, a pointer to the initial element of an array object) : and E2 is an integer, E1[E2] designates the E2-th element of E1 : (counting from zero). Of course, because the + operator is commutative That both these options exist causes me to conclude that, for arrays, parameter passing can be viewed as either arrays by-reference or pointers by-value. I don't understand what relevance type checking has. Since you are choosing to use v as a pointer, one would not expect a type error, yes? Compare: void mumble(void) { char v[42]; char ch = 0; v = ch; } /* error: incompatible types in assignment */ It's because in the previous case v is a pointer that the compiler doesn't mind me assigning to it. In this case, because it's an array, the compiler objects. I guess the case for pass-by-value would be a little stronger because one has to have passing a pointer by value anyway (since pointers are first-class datatypes) and that can be used to describe passing arrays (as you described). The difference is that the /callee/ function is different between the two cases. Also, notice that arrays in expressions turn into pointers in the same way, so function argument passing works the same way as assignment -- a hallmark of pass-by-value. Not in all expressions as you yourself mentioned: int a[10], *ap; sizeof a; sizeof ap; Well done, you've pedantically picked up on the lapse in the first part of the sentence (about sizeof, which as you say I've already mentioned elsewhere) but failed to take any notice of the more important and relevant point in the second half -- that argument passing works the same way as assignment -- for `arrays' just as for other types. void fa(char p[]); void fp(char *p); void fi(int i); char a[10 * sizeof(char *)] = bar, aa[10 * sizeof(char *)] = splat; char *p = foo, *pp = mumble; int i = 42, ii = 69; i = ii; /* assignment of integers */ fi(ii); /* integer call-by-value */ p = pp; /* assignment of pointers */ fp(pp); /* pointer call-by-value */ fa(pp); /* pointer call-by-value again */ a = aa; /* type error: incompatible types in assignment */ a = pp; /* type error: incompatible types in assignment */ p = aa; /* assignment of pointers */ fa(aa); /* pointer call-by-value yet again */ fp(aa); /* pointer call-by-value yet again */ Again, I refer readers to ISO 9899:1999: in particular: * 6.3.2.1p3 (array-to-pointer decay) * 6.5.2.2 (function calls) * 6.5.16 (assignment operator) Since I'm getting thoroughly fed up of repeating myself on this point, I'm simply going to assume, from now on, that anyone who offers a disagreement without citing passages from
Re: why cannot assign to function call
On Jan 10, 1:49 pm, Joe Strout j...@strout.net wrote: Aaron Brady wrote: Aaron Brady wrote: Possible compromise. You can think of functions as mutation-only. You pass the object, and it gets a new (additional) name. The old name doesn't go in. /compromise That's correct. The reference itself is passed in, not the variable (or expression) that held or generated the reference in the calling code. This is very odd, and I see it quite a bit. Me: You pass the object. Joe: That's correct. You pass the reference. What was wrong with my original? I'm saying that I believe your idea was correct, but worded imprecisely (IMHO of course). So I restated what I believed you were saying, using terminology which I believe to be more precise. If nothing else, this gives you the opportunity to say No, that's not what I meant at all. I believe that my summary is exceedingly beginner-oriented, you might say colloquial. Someone who has even a little programming experience might find it childish. This is true. (Technically, instead of variable, we should say LValue here -- there are things slightly more complex than simple variables that can serve as the left-hand side of an assignment. So replace variable with lvalue above if you prefer.) This is a point worth making. I want to penny-pinch every term in an introductory text, though, so, it's a tough call. Agreed. Is a function call an lvalue in Python? If not, descriptors are not functions. M2: If 'fun()' returned a reference, you might be able to mutate the object that refers to. m2: You can sometimes mutate the object it refers to. C2: 'fun()' returns a reference. This is horrendous. http://en.wikipedia.org/wiki/Formal_fallacy http://en.wikipedia.org/wiki/Affirming_the_consequent I did point out that the logic was incorrect (even though the conclusion, in this case, happens to be true).- You've done nothing other than restate your claim; and worse, you disguised doing so in the form of a deduction. A question: Are you Joe and you Mark certain that the simplest possible introductory explanation makes use of the term 'reference'? I am. In any explanation, there is an amount of detail omitted. In an introduction, it's large. (I hold false statements should be avoided, of course, and even misleading ones. However the latter is widely subject-dependent.) Your strategy may have a net positive unifying power, though; and we should see it if so. I may be asking whether it is safe and prudent to avoid (introducing) the term at the level we're looking at (as well as what level exactly it is). Perhaps we can have a contest for shortest/simplest/best description of Python's data/variable/argument model. Sure -- but it judging it might be difficult, requiring some newbies and a test to check their comprehension (as you and I pondered once before). I think the step of least load would be to ask those who are having difficulty what their background is. At worst, they lie; next worst, there's no pattern. Lastly, I don't see any reason why we couldn't make both explanations available. 'For those coming from Java/etc; for those coming from C++/etc.' They would both get read. Yes, except that the reference explanation applies equally well to anyone coming from Java, C/C++, RB, VB.NET, and probably others... I'm not sure to whom the other explanation would appeal, unless perhaps it's LISP programmers (just a guess, since those on that side of the aisle seem to invoke LISP more frequently). True or not, it requires the reader to know what references are. And, since your definition conflicts with the C++ definition, it's not clear that the requirement is good. More terminology: is 'a' a variable, a reference, or an object, in 'a=2'? It's a variable; it refers to an object. It's a variable, which refers to an object. It's a variable, which is a reference to an object. It's a reference to an object. Similarly, is '2' an expression, a reference, or an object? It's an expression; it evaluates to an object. It's an expression; it evaluates to a reference to an object. It's an expression; it expresses an object. It's an expression; it refers to an object. It's a reference to an object. It's an object. I'd like to recommend we decide on unique answers, or avoid ambiguity another way. digression In the case of the Socratic, non-bludgeoning, dialogue with a student, if the student can be trusted to question intelligently, s/he can be expected, on our telling him/er, 'a' is an object, to ask, What object? Whether to expect audience interaction, and what interaction, is a big component in the choice of method of a demonstration. /digression digression 300+301+302 is 300+301+302 False There are 10 objects created in the evaluation of this, 11 if including 'False'. They (their integer contents), in order, are: 300, 301, 302, then 300, 301, 302, not the same ones, again, then
Re: why cannot assign to function call
On Sun, 11 Jan 2009 03:25:24 +, Mark Wooding wrote: Steven D'Aprano st...@remove-this-cybersource.com.au wrote: I don't believe it is a red-herring. As I understand it, Mark and Joe insist that C is pass-by-value *even in the case of arrays*, despite the semantics of array passing being identical to the semantics of pass-by- reference in (say) Pascal. But they aren't. I've provided several examples to show this. Here's some p-b-r code in Pascal: program main(input, output); type arraytype = array[1..2] of integer; var arr: arraytype; procedure foo(var bar: arraytype); begin bar[2] := 42; writeln(' bar = ', bar[1], ' ', bar[2]); end; begin arr[1] := 0; arr[2] := 0; writeln(' Before: arr = ', arr[1], ' ', arr[2]); foo(arr); writeln(' After: arr = ', arr[1], ' ', arr[2]); end. Here's the output from running that code: Before: arr = 0 0 bar = 0 42 After: arr = 0 42 Here's the equivalent C code, as close to an exact one-to-one correspondence as I can come up with: #includestdio.h void foo(int bar[2]) { bar[1] = 42; printf(\n bar = %d %d , bar[0], bar[1]); } int main() { int arr[2]={0,0}; printf(\n Before: arr = %d %d , arr[0], arr[1]); foo(arr); printf(\n After: arr = %d %d , arr[0], arr[1]); printf(\n); return 0; } And the output of it: Before: arr = 0 0 bar = 0 42 After: arr = 0 42 If it walks like pass-by-reference, and smells like pass-by-reference, and swims like pass-by-reference, is it still your contention that it is pass-by-value? For the purpose of clearing this up once and for all: arrays, in C, are `don't-pass-at-all'. There is no way -- none whatever -- of declaring a function parameter as having array type. It looks just like I did precisely that here: void foo(int bar[2]) I guess this is where you explain again that arrays in C are bizarre, and that while int arr[2] inside a function body means declare an array of two ints and call it 'arr', the exact same declaration in a function parameter list means something else. Possibly: Declare a pointer which points to an array of two ints, call the pointer 'arr', and dereference the pointer every time it is used inside the function as if you were referring to the array directly. Am I close? -- Steven -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Steven D'Aprano st...@remove-this-cybersource.com.au writes: If it walks like pass-by-reference, and smells like pass-by-reference, and swims like pass-by-reference, is it still your contention that it is pass-by-value? Of course the C example is pass by value. It's just that the value being passed is an address. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Jan 11, 8:32 am, Paul Rubin http://phr...@nospam.invalid wrote: Steven D'Aprano st...@remove-this-cybersource.com.au writes: If it walks like pass-by-reference, and smells like pass-by-reference, and swims like pass-by-reference, is it still your contention that it is pass-by-value? Of course the C example is pass by value. It's just that the value being passed is an address. I believe, that in order for the C example to be p-b-r, you would need the following: void foo(var int bar[2]) { bar = malloc( something ); } Such that the caller reflects the change. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Sun, 11 Jan 2009 06:32:31 -0800, Paul Rubin wrote: Steven D'Aprano st...@remove-this-cybersource.com.au writes: If it walks like pass-by-reference, and smells like pass-by-reference, and swims like pass-by-reference, is it still your contention that it is pass-by-value? Of course the C example is pass by value. It's just that the value being passed is an address. I believe that's exactly the way VAR (reference) parameters are implemented in Pascal. What's the difference? -- Steven -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Aaron Brady castiro...@gmail.com wrote: True or not, it requires the reader to know what references are. And, since your definition conflicts with the C++ definition, it's not clear that the requirement is good. I blame C++ for coopting a perfectly good word with a established well-understood meaning, and applying it to a half-baked and inconsistent concept. More terminology: is 'a' a variable, a reference, or an object, in 'a=2'? It's a variable; it refers to an object. It's a variable, which refers to an object. It's a variable, which is a reference to an object. It's a reference to an object. It's an /identifier/ (2.3); or, if you want a shorter word, a /name/ (also 2.3). (I'll go with `name'.) In any given context, the name might or might not mean something. Names are assigned meanings by an /environment/; if the environment assigns a meaning to a name, we say that the name is /bound/ to that meaning; otherwise it is /unbound/. The rules for determining the environment at any given point in a program vary with language. Python is fairly simple: it is /lexically scoped/, which means that a great deal of information about the environment can be determined simply by analysing the program text statically. (Compare `dynamic scope', where the environment at any point in a program's executiondepends quite subtly on which functions are active at that point. The main difference is that, under lexical scoping, a function's environment is an extension of the environment in which it was defined, whereas under dynamic scoping a function's environment is an extension of the environment of the caller. Languages can offer both kinds of scoping simultaneously: both Common Lisp and Perl do this.) Python is /block-structured/: various syntactic forms (/blocks/) introduce new environments which are extensions of the environment of the enclosing block, i.e., within the inner block, a few names are bound to new meanings, while all the other names retain whatever meaning they had in the enclosing block. Names whose meanings are modified are said to be /bound by the block/. A name which occurs within the block is a /bound occurrence/ if the name is bound by the block, or a /free occurrence/ otherwise. Python has what one might call `implicit binding': a name appearing alone (or as part of a destructuring pattern) on the left-hand side of an assignment which would otherwise be a free occurrence, and in the absence of an explicit declaration such as `global' or `nonlocal', is implicitly bound to a fresh variable by the smallest enclosing block. Python also has explicit binding: e.g., the parameter names of a function are explicitly bound to new variables by the function. So, where were we? `a = 2'. The `a' is a name. It appears alone on the left-hand side of an assignment: this is therefore a candidate for implicit binding. Let's assume that `a' is bound to a variable, either as a result of this or some other implicit binding, or an explicit declaration. (I don't believe Python has meanings other than variables which might be bound to a name, so this is pretty safe. Scheme, for example, puts macros and special syntactic keywords in the same namespace as variables, so you can, for example, lexically rebind the IF keyword as a function, should you so wish. This is probably a bad idea.) So, `a' is bound to a variable. The variable, like all Python variables, stores a reference. We don't know what this reference might be before the assignment, but afterwards, we know that it must be a reference to an integer object representing the value 2. This is cumbersome to talk about; in informal conversations, one often talks about `the variable a' or even `the integer a'. It's important to realise that such phrases are abbreviations for convenience, and do not directly correspond to reality. Similarly, is '2' an expression, a reference, or an object? It's an expression; it evaluates to an object. It's an expression; it evaluates to a reference to an object. It's an expression; it expresses an object. It's an expression; it refers to an object. It's a reference to an object. It's an object. It's an /integer literal/ (2.4.3). It's also an expression, because all literals are expressions. We can confidently predict that the value of this expression (i.e., the result of evaluating it) is an integer object representing the value 2. In the case of the Socratic, non-bludgeoning, dialogue with a student, if the student can be trusted to question intelligently, s/he can be expected, on our telling him/er, 'a' is an object, to ask, What object? Whether to expect audience interaction, and what interaction, is a big component in the choice of method of a demonstration. Declaring `a' to be an object begs many questions, such as `what type does this object have?'. This is an unfortunate question, because the naive answer (e.g., from the above: `it's an integer') comes up against the problem
Re: why cannot assign to function call
Steven D'Aprano st...@remove-this-cybersource.com.au wrote: I guess this is where you explain again that arrays in C are bizarre, and that while int arr[2] inside a function body means declare an array of two ints and call it 'arr', the exact same declaration in a function parameter list means something else. I quoted 6.7.5.3p7 before. Please pay attention. : A declaration of a parameter as ``array of type'' shall be adjusted to : ``qualified pointer to type'', where the type qualifiers (if any) are : those specified within the [ and ] of the array type derivation. If : the keyword static also appears within the [and ] of the array type : derivation, then for each call to the function, the value of the : corresponding actual argument shall provide access to the first : element of an array with at least as many elements as specified by the : size expression. Declare a pointer which points to an array of two ints, call the pointer 'arr', and dereference the pointer every time it is used inside the function as if you were referring to the array directly. No. Declare a pointer which points to an int, and call the pointer `arr'. Am I close? Vaguely. A pointer to an array is not the same as a pointer to its first element. The former has type T (*)[N]; the latter has type T *. The compiler entirely ignores the number (unless you write `static' -- little-known C99 feature). There is no further magic: it's just a pointer. -- [mdw] -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Mark Wooding wrote: ru...@yahoo.com ru...@yahoo.com wrote: Mark Wooding wrote: ru...@yahoo.com ru...@yahoo.com wrote: ... For another: static void bar(char v[]) { char ch = 0; v = ch; } /* type error if arrays truly passed by reference */ v can be used as an array reference, e.g. v[1] = 23 exactly as in the pass-by-reference fortran example. And v can also be used as a local variable and reassigned. If the first option was the only one, would you not say C was definitely pass-by-reference (for arrays)? If one could only use v as a pointer (i.e. access the argument array as *(v+1) = 23, then I would say that arrays are not passed at all, only pointers by-value. Your knowledge of C is clearly limited, at best. The notations *(p + i) and p[i] are entirely equivalent in every way, since the square-brackets indexing operator is in fact defined in terms of pointer dereference (6.5.2.1): ... Yes, my C experience is limited. I very rarely write any C these days and even many years ago when I wrote more, it was very vanilla stuff not requiring any deep knowlage of dark corners of the language. Nevertheless, I am aware of everything you wrote above (the gist, not the standard's verbiage) and the common demonstration of it: 3[surprised?] = 'p'. But if you'll note, I said if ... referring to a couple of hypothetical C-like languages, so your chapter-and-verse quote from the standard, while interesting and appreciated, was basically irrelevant. ... Also, notice that arrays in expressions turn into pointers in the same way, so function argument passing works the same way as assignment -- a hallmark of pass-by-value. Not in all expressions as you yourself mentioned: int a[10], *ap; sizeof a; sizeof ap; Well done, you've pedantically picked up on the lapse in the first part It isn't pedantic. It is the only thing (IFAIK) that allows one to talk about variables of an array type (as opposed to only pointers). ... Since I'm getting thoroughly fed up of repeating myself on this point, I'm simply going to assume, from now on, that anyone who offers a disagreement without citing passages from (at least a specific draft of) ISO 9899 to support any claims made is simply being wilfully ignorant -- and therefore no longer worth my attention. (If you don't have a copy of any such version, may I suggest http://std.dkuug.dk/jtc1/sc22/open/n2794/ citing this (confusingly) as n843, as a place to start.) Actually I've already downloaded a C89/90 standard draft in the hope that an earlier version would be simpler. Since C's behavior vis call-by-x is (I presume) unchanged back to KR, whatever it said about it should be confirmable by any ealier versions descriptions of C. But more importantly, we seem to be talking at cross purposes. My idea of a language model is a description of the language that is precise enough to allow a model's user to predict how any piece of code will behave when run. The C-standard(s) do that for C. Since many people would claim that a language that does not behave as the C standard describes is not C, it is definitional, and one cannot reasonably say that the model presented in the C standard is incorrect. (Of course, like all standards, the C standard is not perfect, it contains errors and inconsistencies that require correction, and like all languages, C evolves requiring new versions of the standard.) Nevertheless, I see no reason to believe, based on anything said in this thread, or my experience in general, that there can be *only* one correct model of a language, or part of a language. So all the quoting of the C standard in the world does not address the issue: can C's array passing be described (modeled) as pass-by-reference? I acknowledge that the C standard provides one (correct) model in which arrays aren't passed, only pointers by-value. To address the issue you need to show that an alternate model (array passing by-reference for example) is incorrect, i.e. leads it user to conclude that a program will behave differently than it does. And of course, when such a discrepancy model and behavior is pointed out, the proposer of the alternate model will fix it if possible. If you can not show that an alternate model is incorrect, then the next best thing is to show that your model is better. Better would involve considerations such as simpler, consistent with other languages, easier to understand by various categories of users, etc. Obviously this is a very subjective task, and one could expect disagreement. Many of the responses by you, Joe, and others, have an of course this model is the right one, how could anyone think otherwise tone to them. I hope I have explained clearly why I think that attitude is unjustified. Finally, and please don't be insulted by this, but to me and many readers, you are a bunch on glowing dots on a screen. I have been using the internet long enough to have any number of times read very
Re: why cannot assign to function call
ru...@yahoo.com ru...@yahoo.com wrote: But if you'll note, I said if ... referring to a couple of hypothetical C-like languages, so your chapter-and-verse quote from the standard, while interesting and appreciated, was basically irrelevant. Ah, what you actually said was still quoted above (thanks): : v can be used as an array reference, e.g. v[1] = 23 exactly as in : the pass-by-reference fortran example. And v can also be used as a : local variable and reassigned. : : If the first option was the only one, would you not say C was : definitely pass-by-reference (for arrays)? I must have been reading too quickly and I missed the conditional here. I'm terribly sorry. :-( Answering the question as posed, rather than mis-read: I think the sizeof business would still indicate that a pointer was being passed. If the language also forbade sizeof on array-type arguments and passing array as arguments to functions expecting pointers, then I'd say you'd have a point. But I'm not sure I'd agree that such a language was `C-like' -- the bizarre behaviour of arrays is one of the language's most distinctive features! Also, notice that arrays in expressions turn into pointers in the same way, so function argument passing works the same way as assignment -- a hallmark of pass-by-value. Not in all expressions as you yourself mentioned: int a[10], *ap; sizeof a; sizeof ap; Well done, you've pedantically picked up on the lapse in the first part It isn't pedantic. It is the only thing (IFAIK) that allows one to talk about variables of an array type (as opposed to only pointers). You can talk about /variables/ of array type, but not /expressions/ of array type (except in very limited circumstances, as operands of sizeof or ). Actually I've already downloaded a C89/90 standard draft in the hope that an earlier version would be simpler. Since C's behavior vis call-by-x is (I presume) unchanged back to KR, whatever it said about it should be confirmable by any ealier versions descriptions of C. It should be; but you may have to adjust the clause numbers I'm citing. Also, the earlier version of the standard didn't have numbered paragraphs, which makes citing specific bits more tedious. But more importantly, we seem to be talking at cross purposes. We might be... [snip] So all the quoting of the C standard in the world does not address the issue: can C's array passing be described (modeled) as pass-by-reference? I acknowledge that the C standard provides one (correct) model in which arrays aren't passed, only pointers by-value. To address the issue you need to show that an alternate model (array passing by-reference for example) is incorrect, i.e. leads it user to conclude that a program will behave differently than it does. And of course, when such a discrepancy model and behavior is pointed out, the proposer of the alternate model will fix it if possible. I'd claim, in fact, that the proposer of an alternative model bears the responsibility to prove it correct, i.e., in the case of C, at least consistent with the official standard. But I believe that I have indeed shown that the arrays-passed-by- reference model is incorrect, by demonstrating that the called function sees the parameter as a freshly allocated pointer and not an array: it can be assigned (unlike an array); its size is that of a pointer; one can take its address and manipulate its bit-pattern as if it were a pointer. Indeed, the standard guarantees that no strictly-conforming C program can distinguish the parameter from a freshly allocated pointer. And therefore I can point to my earlier (almost-formal) definitions of pass-by-value and pass-by-reference to complete the proof: the allocation of a new variable is inconsistent with pass-by-reference, while it is consistent with pointer-decay followed by pass-by-value. Of course, this is contingent on your acceptance of my definitions. But I don't see anyone else providing sufficiently detailed definitions to make such proofs, and my definitions seem consistent with the way most other languages use the terms, so I'm pretty confident that they're good... Many of the responses by you, Joe, and others, have an of course this model is the right one, how could anyone think otherwise tone to them. I hope I have explained clearly why I think that attitude is unjustified. I hope that I've been doing more than that: drawing comparisons with and borrowing concepts from other languages, attempting to solidify definitions which otherwise seem to cause controversy, and then drawing conclusions from these definitions to strengthen my case and weaken others' cases. Finally, and please don't be insulted by this, but to me and many readers, you are a bunch on glowing dots on a screen. As are we all. (Brought to you by Sobering Thought for the Day.) I have been using the internet long enough to have any number of times read very plausible,
Re: why cannot assign to function call
On Fri, 09 Jan 2009 20:23:11 +, Mark Wooding wrote: Steven D'Aprano st...@remove-this-cybersource.com.au wrote: I'm pretty sure that no other pure-Python coder has manipulated references either. They've manipulated objects. No: not directly. The Python program deals solely with references; anything involving actual objects is mediated by the runtime. Your claim is ambiguous: when you say the Python program, are you talking about the program I write using the Python language, or the Python VM, or something else? If the first, then you are wrong: the Python program I write doesn't deal with references. It deals with objects. This discussion flounders because we conflate multiple levels of explanation. People say You do foo when they mean the Python VM does foo. Earlier, I responded to your claim that I was storing references by saying I was pretty sure I didn't store references, and gave an example of the line of Python code x=23. Your response was to mix explanatory levels: You bind names to locations which store immediate representations. Python IRs are (in the sense defined above) exclusively references. I most certainly don't bind names to locations. When I write x=23, I don't know what the location of the object 23 is, so how could I bind that location to the name? You are conflating what the Python VM does with what I do. What *I* do is bind the object 23 to the name x. I don't know the location of 23, I don't even know if 23 has a well-defined location or if it is some sort of distributed virtual data structure. As a Python programmer, that's the level I see: names and objects. At a lower level, what the Python VM does is store the name 'x' in a dictionary, bound to the object 23. No locations come into it, because at this level of explanation, dictionaries are an abstract mapping. There's no requirement that the abstract dictionary structure works by storing addresses. All we know is that it maps the name 'x' to the object 23 somehow. Maybe there's no persistent storage, and the dict stores instructions telling the VM how to recreate the object 23 when it is needed. Who knows? But at this explanatory level, there are no locatives involved. Names and objects float as disembodied entities in the aether, and dicts map one to the other. At an even lower explanatory level, the CPython implementation of dictionaries works by storing a pointer (or if you prefer, a reference) to the object in a hash table. Pointers, of course, are locatives, and so finally we come to the explanation you prefer. We've gone from abstract names-and-classes to concrete pointers-to-bytes. But this is at least two levels deeper than what's visible in Python code. Just about the only time Python coders work with locatives is when they manually calculate some index into a string or list, or similar. At an even lower explanatory level, all the VM does is copy bytes. And at a lower level still, it doesn't even copy bytes, it just flips bits. And below that, we're into physics, and I won't go there. I daresay you probably get annoyed at me when I bring up explanations at the level of copying bytes. You probably feel that for the average Python programmer, *most of the time* such explanations are more obfuscatory than useful. Of course, there are exceptions, such as explaining why repeated string concatenation is likely to be slow. There is a time and a place for such low level explanations, but not at the high-level overview needed by the average Python programmer. And you would be right. But I argue that your explanation at the level of references is exactly the same: it is too low level. It relies on specific details which may not even be true for all implementations of Python. It certainly relies on details which won't be true for hypothetical versions of Python running on exotic hardware. One can do massively parallel calculations using DNA, and such DNA computers are apparently Turing complete. I have no idea how one would write a Python virtual machine in such a biological computer, but I'm pretty sure that data values won't have well-defined locations in a machine that consists of billions of DNA molecules floating in a liquid. If that's too bizarre for you, think about simulating a Python VM in your own head. If we know one thing about the human brain, it is that thoughts and concepts are not stored in single, well-defined locations, so when you think of x=23, there is no pointer to a location in your head. That's why we should try to keep the different layers of explanation separate, without conflating them. Python programmers don't actually flip bits, and neither do they manipulate references. Python programmers don't have access to bits, or references. What they have access to is objects. No, that's my point: Python programmers /don't/ have direct access to objects. The objects themselves are kept at arm's length by the indirection
Re: why cannot assign to function call
On Jan 9, 9:30 am, Joe Strout j...@strout.net wrote: Aaron Brady wrote: Possible compromise. You can think of functions as mutation-only. You pass the object, and it gets a new (additional) name. The old name doesn't go in. /compromise That's correct. The reference itself is passed in, not the variable (or expression) that held or generated the reference in the calling code. This is very odd, and I see it quite a bit. Me: You pass the object. Joe: That's correct. You pass the reference. What was wrong with my original? This is true. (Technically, instead of variable, we should say LValue here -- there are things slightly more complex than simple variables that can serve as the left-hand side of an assignment. So replace variable with lvalue above if you prefer.) This is a point worth making. I want to penny-pinch every term in an introductory text, though, so, it's a tough call. M2: If 'fun()' returned a reference, you might be able to mutate the object that refers to. m2: You can sometimes mutate the object it refers to. C2: 'fun()' returns a reference. This is horrendous. http://en.wikipedia.org/wiki/Formal_fallacy http://en.wikipedia.org/wiki/Affirming_the_consequent A question: Are you Joe and you Mark certain that the simplest possible introductory explanation makes use of the term 'reference'? Perhaps we can have a contest for shortest/simplest/best description of Python's data/variable/argument model. Lastly, I don't see any reason why we couldn't make both explanations available. 'For those coming from Java/etc; for those coming from C++/etc.' They would both get read. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
[Another tome. I hope this contains sufficient new material to continue to be of interest to other readers.] Steven D'Aprano st...@remove-this-cybersource.com.au wrote: On Fri, 09 Jan 2009 20:23:11 +, Mark Wooding wrote: No: not directly. The Python program deals solely with references; anything involving actual objects is mediated by the runtime. Your claim is ambiguous: when you say the Python program, are you talking about the program I write using the Python language, or the Python VM, or something else? The former. If the first, then you are wrong: the Python program I write doesn't deal with references. It deals with objects. Do you have evidence for this claim other than your vigorous assertion? I have evidence: in particular, after the example l = [1, 2, 3] l[1] = l the list l (abuse of terminology: I mean `the list referred to from the location bound to l') has the structure I drew, but you snipped: ,--, v | +---++---+| l: | * | *- 1 | +---++---+| | *--' +---+ | *- 3 +---+ Can you explain, without using the concept of references (or anything else equivalent) why this happens, and why the result is not (for example) [1, [1, 2, 3], 3]? This discussion flounders because we conflate multiple levels of explanation. No. I'm sticking to a single level of explanation. It may appear to be a lower level than the one you're trying to promote, and therefore contains more detail and is more complicated, but it has the benefit that it explains observed phenomena. People say You do foo when they mean the Python VM does foo. Since what the programmer does is type at a keyboard, the keystrokes being interpreted eventually as editing commands in a text editor (having been processed by various intermediate pieces of software probably including keyboard drivers, and maybe window systems or terminal line disciplines), anything else is an abuse of terminology; but a useful one. So, the programmer types, with the objective (presumably) of constructing a text file containing a Python program which, when executed by an appropriate Python implementation, will behave in a satisfactory way. The notion of `Python VM' is a good one (though potentially open to confusion -- alas, `virtual machine' has multiple meanings too). So: a Python program can be interpreted as instructions to such a virtual machine, to behave in particular ways. Agreed? The question is: how do we best describe the behaviour of such a Python VM when executing a Python program, given its text? It's far too easy, as we've both shown, to slip into imprecise terminology here. When I write You bind names to locations which store immediate representations. Python IRs are (in the sense defined above) exclusively references. I mean, of course, that a Python implementation behaves is if it binds `names to locations which store immediate representations'. I most certainly don't bind names to locations. When I write x=23, I don't know what the location of the object 23 is, so how could I bind that location to the name? Quite. A lapse on my part, due to sloppy writing caused by a desire not to make this article any longer than it already was. You are conflating what the Python VM does with what I do. No... What *I* do is bind the object 23 to the name x. What you do is described above. It doesn't involve any binding at all, or objects. Unless, that is, you are actually implementing Python personally (i.e., not merely instructing a computer to do so, but actually within your own mind); in which case I claim that you must do so as I have described, or do so wrongly. I don't know the location of 23, I don't even know if 23 has a well-defined location I warned that I was using certain terms in a technical sense, and attempted to define them clearly at the top of my article. The word `location' was one such: : * A /location/[1] is an area of memory suitable for storing the : /immediate representation/ (which I shall abbreviate to /IR/) of a : value. (A location may be capable of storing things other than : IRs, e.g., representations of unevaluated expressions in lazily : evaluated languages. Locations may vary in size, e.g., in order : to be capable of storing different types of IRs.) Whether 23 has a location is unimportant. In the context of your example `x = 23', what's important is that the name `x' is bound to a location, and then an immediate representation of the value `23' is stored in that location. (This description holds for all languages I can think of, modulo details of which occurrences of names are binding occurrences, and C++'s user- controlled assignment and so on.) or if it is some sort of distributed virtual data structure. As a Python programmer, that's the level I see: names and
Re: why cannot assign to function call
ru...@yahoo.com ru...@yahoo.com wrote: Agreed. I think the docs, especially those that develop the conceptual model of how Python work at runtime, could use some major attention. If we can achieve consensus in this (still remarkably civil) discussion, we might be able to amend the docs. I would be willing to bet that most of the confused posters do not distinguish between assignment operation (AO) and data model (DM). Their conceptual box is labeled assignment behavior and includes both AO and DM. They expect that AO+DM in Python will produce the same results in Python as they are used to in the other languages they've used. That the discrepancy comes from the DM part rather than the AO part is pretty irrelevant to them given that world view. I think that we're in agreement, up to this point at least. That's good. My claim is that, until they learn to distinguish these two aspects of Python's semantics, they will remain confused. -- [mdw] -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Aaron Brady wrote: Lastly, I don't see any reason why we couldn't make both explanations available. 'For those coming from Java/etc; for those coming from C++/etc.' They would both get read. That's what I was just thinking .. there are lots of others, too: for those coming from relational database theory... would be a good one. Mel. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Jan 9, 6:47 am, Mark Wooding m...@distorted.org.uk wrote: ru...@yahoo.com ru...@yahoo.com wrote: As a side comment (because it always bugs me when I read this, even though I read it in very authoritative sources), ISTM that C passes everything by value except arrays; they are passed by reference (by passing a pointer to the array by value.) Admittedly, the close relationship between arrays and pointers makes it easy conflate them. Arrays are distinctly second-class citizens in C. Basically, in C, arrays aren't passed at all but there's some syntactic sugar so you can squint and con yourself that they're passed by reference. If you try to pass an array, the array name immediately decays to a pointer, and the pointer gets passed instead -- by value. The corresponding function parameter must be a pointer to an approrpriate kind of thing, though you're allowed to write []s to confuse yourself if you like -- T D[] in a function parameter declaration means precisely the same as T *D -- to the extent that D has type T **D and so on. What is the observable difference between converting an array to a reference (pointer) to that array and passing the reference by value, and passing the array by reference? That is, given a C-like compiler that is the same as C except that it passes arrays by reference, how would it differ from an ordinary C compiler? The choice of terminology (in this case) seems to me to be a matter of convention rather than any fundamental observable difference. I guess the case for pass-by-value would be a little stronger because one has to have passing a pointer by value anyway (since pointers are first-class datatypes) and that can be used to describe passing arrays (as you described). Adding a second mechanism, passing-arrays-by-reference, is perhaps unnecessary, but not wrong, and may be more easily understandable to the target audience. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
ru...@yahoo.com ru...@yahoo.com wrote: What is the observable difference between converting an array to a reference (pointer) to that array and passing the reference by value, and passing the array by reference? For one: #include stdio.h static size_t foo(char v[]) { return sizeof v; } int main(void) { char v[sizeof(char *) * 10]; puts(foo(v) == sizeof(char *) ? pointer : reference); return (0); } For another: static void bar(char v[]) { char ch = 0; v = ch; } /* type error if arrays truly passed by reference */ I guess the case for pass-by-value would be a little stronger because one has to have passing a pointer by value anyway (since pointers are first-class datatypes) and that can be used to describe passing arrays (as you described). The difference is that the /callee/ function is different between the two cases. Also, notice that arrays in expressions turn into pointers in the same way, so function argument passing works the same way as assignment -- a hallmark of pass-by-value. -- [mdw] -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
ru...@yahoo.com wrote: What is the observable difference between converting an array to a reference (pointer) to that array and passing the reference by value, and passing the array by reference? The difference is whether an assignment to the formal parameter (within the function) affects the actual parameter (in the calling code). If it does, then that's pass by reference. If it does not, then that's pass by value. That is, given a C-like compiler that is the same as C except that it passes arrays by reference, how would it differ from an ordinary C compiler? Such a compiler is available: it's called C++, and it gives the programmer the choice to pass by value or pass by reference (the latter indicated by adding to the parameter in the function declaration, just like you would add ByRef in RB or VB.NET). If the parameter is called foo, and you pass in bar, then foo = SomeNewArray(); would change bar if it were passed by reference; it would not affect bar at all if it were passed by value. The two are quite distinct. Best, - Joe -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Aaron Brady wrote: Aaron Brady wrote: Possible compromise. You can think of functions as mutation-only. You pass the object, and it gets a new (additional) name. The old name doesn't go in. /compromise That's correct. The reference itself is passed in, not the variable (or expression) that held or generated the reference in the calling code. This is very odd, and I see it quite a bit. Me: You pass the object. Joe: That's correct. You pass the reference. What was wrong with my original? I'm saying that I believe your idea was correct, but worded imprecisely (IMHO of course). So I restated what I believed you were saying, using terminology which I believe to be more precise. If nothing else, this gives you the opportunity to say No, that's not what I meant at all. This is true. (Technically, instead of variable, we should say LValue here -- there are things slightly more complex than simple variables that can serve as the left-hand side of an assignment. So replace variable with lvalue above if you prefer.) This is a point worth making. I want to penny-pinch every term in an introductory text, though, so, it's a tough call. Agreed. M2: If 'fun()' returned a reference, you might be able to mutate the object that refers to. m2: You can sometimes mutate the object it refers to. C2: 'fun()' returns a reference. This is horrendous. http://en.wikipedia.org/wiki/Formal_fallacy http://en.wikipedia.org/wiki/Affirming_the_consequent I did point out that the logic was incorrect (even though the conclusion, in this case, happens to be true). A question: Are you Joe and you Mark certain that the simplest possible introductory explanation makes use of the term 'reference'? I am. Perhaps we can have a contest for shortest/simplest/best description of Python's data/variable/argument model. Sure -- but it judging it might be difficult, requiring some newbies and a test to check their comprehension (as you and I pondered once before). Lastly, I don't see any reason why we couldn't make both explanations available. 'For those coming from Java/etc; for those coming from C++/etc.' They would both get read. Yes, except that the reference explanation applies equally well to anyone coming from Java, C/C++, RB, VB.NET, and probably others... I'm not sure to whom the other explanation would appeal, unless perhaps it's LISP programmers (just a guess, since those on that side of the aisle seem to invoke LISP more frequently). Best, - Joe -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Sat, 10 Jan 2009 12:52:47 -0700, Joe Strout wrote: What is the observable difference between converting an array to a reference (pointer) to that array and passing the reference by value, and passing the array by reference? The difference is whether an assignment to the formal parameter (within the function) affects the actual parameter (in the calling code). If it does, then that's pass by reference. If it does not, then that's pass by value. Such a definition is incomplete. You are mischaracterising call-by- reference as defined by a single binary state: assignment either affects the caller, or it doesn't. If it does, it is p-b-r, if it doesn't, it's p- b-v. According to this definition, there are no other argument passing strategies possible. That's an enormously broad brush by which you sweep away decades of comp sci terminology. Not just Barbara Liskov and pass-by- object, but Algol's thunk-based pass-by-name and Haskell's pass-by-need. In other words, you have created a false dichotomy between pass-by- reference and pass-by-value. There are actually three fundamental characteristics of pass-by-reference: * passing a value by reference does not lead to the value being copied, in contrast with pass-by-value where it does; * modifications to the value passed by reference are visible to the caller; * assignments to the value passed by reference are visible to the caller. Pascal VAR parameters have all three characteristics. Pascal non-VAR parameters have none of them. Python parameters have two of the three. C parameters (call-by-value) have none of them, except for arrays, where they have all three, making arrays in C behave just like Pascal pass-by- reference VAR parameters. -- Steven -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Sat, 10 Jan 2009 18:44:37 -, ru...@yahoo.com wrote: What is the observable difference between converting an array to a reference (pointer) to that array and passing the reference by value, and passing the array by reference? This is a red herring, though. From either viewpoint, C arrays are anomalous in comparison with other C data types. -- Rhodri James *-* Wildebeeste Herder to the Masses -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Steven D'Aprano st...@remove-this-cybersource.com.au wrote: There are actually three fundamental characteristics of pass-by-reference: * passing a value by reference does not lead to the value being copied, in contrast with pass-by-value where it does; * modifications to the value passed by reference are visible to the caller; * assignments to the value passed by reference are visible to the caller. I've given an extensive definition of pass-by-reference. Distilling and paraphrasing, the main characteristic is that (where possible) new variables are not created: rather, the parameter names are bound to the caller's variables. All three of your above characteristics are consequences of this one. But your definition is also flawed: it doesn't distinguish pass-by- reference from pass-by-value/return (where the caller's variable is updated from the function parameter's final value when the function returns). The difference is detectable if you use global variables, however: variable g = 1 function foo(x): x = 2 print g foo(g) print g prints 1 and 1 if you use pass-by-value, 2 and 2 if you use pass-by- reference, and 1 and 2 if you use pass-by-value/result. (Within the framework I presented elsewhere, pass-by-value/result is like pass-by-value, except that we update: s = s'''[s'''(e''(n))/l] where s''' is the store just prior to the return, s is the store just after the return, n is the parameter name, e'' is the function's environment, and l is the location designated by the argument expression, i.e., (l, s') = loc(x, e, s). If there is no such location, then the language may either fail to compile the call, or omit the update operation.) Pascal VAR parameters have all three characteristics. That's because they work as I've suggested. Pascal non-VAR parameters have none of them. Indeed. Python parameters have two of the three. C parameters (call-by-value) have none of them, except for arrays, where they have all three, making arrays in C behave just like Pascal pass-by- reference VAR parameters. Rubbish. In C: void foo(char v[42]) { v = 0; } Calling this function has no effect on the caller whatsoever. I've already explained C arrays, with reference to the ISO standard, exhaustively: there should be no excuse for continued misunderstanding of this point. -- [mdw] -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Sun, 11 Jan 2009 01:22:48 +, Rhodri James wrote: On Sat, 10 Jan 2009 18:44:37 -, ru...@yahoo.com wrote: What is the observable difference between converting an array to a reference (pointer) to that array and passing the reference by value, and passing the array by reference? This is a red herring, though. From either viewpoint, C arrays are anomalous in comparison with other C data types. I don't believe it is a red-herring. As I understand it, Mark and Joe insist that C is pass-by-value *even in the case of arrays*, despite the semantics of array passing being identical to the semantics of pass-by- reference in (say) Pascal. While Mark is willing to admit that arrays are bizarre (his term) in C, I don't think he accepts that passing arrays in C is anything but pass-by-value. I think this gets very close to the bone of the debate. It demonstrates that pass-by-value as Mark and Joe understand it is such a broad concept that it can describe any argument passing behaviour at all and therefore is meaningless. -- Steven -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Steven D'Aprano st...@remove-this-cybersource.com.au wrote: I don't believe it is a red-herring. As I understand it, Mark and Joe insist that C is pass-by-value *even in the case of arrays*, despite the semantics of array passing being identical to the semantics of pass-by- reference in (say) Pascal. But they aren't. I've provided several examples to show this. Most tellingly, the parameter types are different in the two cases. While Mark is willing to admit that arrays are bizarre (his term) in C, I don't think he accepts that passing arrays in C is anything but pass-by-value. For the purpose of clearing this up once and for all: arrays, in C, are `don't-pass-at-all'. There is no way -- none whatever -- of declaring a function parameter as having array type. Furthermore, if (say) an argument expression consists only of an identifier bound to an object of array type, the resulting argument expression has pointer type, and is fully evaluated prior to the sequence point before the function call. I provided chapter and verse elsewhere, and I'm getting rather fed up of repeating myself. Certainly a dissenting opinion should include references to at least a (specific) draft of the ISO C standard. I think this gets very close to the bone of the debate. It demonstrates that pass-by-value as Mark and Joe understand it is such a broad concept that it can describe any argument passing behaviour at all and therefore is meaningless. I've proven (not quite rigorously, but sketched a proof, and provided definitions which are now sufficiently formal that you can fill in the details) that my definition of pass-by-value excludes certain behaviours. But, most significantly, you cannot implement `swap' given only pass-by-value. -- [mdw], wondering whether Hanlon's razor is getting blunt. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Mark Wooding wrote: ru...@yahoo.com ru...@yahoo.com wrote: What is the observable difference between converting an array to a reference (pointer) to that array and passing the reference by value, and passing the array by reference? For one: #include stdio.h static size_t foo(char v[]) { return sizeof v; } int main(void) { char v[sizeof(char *) * 10]; puts(foo(v) == sizeof(char *) ? pointer : reference); return (0); } You are saying that because the size of the argument (10) is not available in the function, it cannot be call-by-reference? I think fortran is accepted as the archetypal call-by- reference language and it does not automatically supply argument size information to functions. In fortran, if the size of the argument is known at compile time, the programmer explicitly declares the parameter with the same size in the function. integer k(3) call mysub (k) write (unit=*, fmt=*) k(1),k(2),k(3) end subroutine mysub (x) integer x(3) do 100, i=1,3 100 x(i) = i return end If not, he passes the size explicitly as an argument: integer k(3) call mysub (k, 3) write (unit=*, fmt=*) k(1),k(2),k(3) end subroutine mysub(x, n) integer x(0) do 100, i=1,3 100 x(i) = i return end Obviously both these idioms translate directly into C. Here is the second: #include stdio.h void mysub (int k[], int n) { int i; for (i=0; i3; i++) { k[i] = i; } return; } main() { int k[3]; mysub (k, 3); printf (%i,%i,%i\n, k[0],k[1],k[2]); } So if fortran can be call-by-reference without the compiler passing size information, I don't see why the above C code can't be as well. For another: static void bar(char v[]) { char ch = 0; v = ch; } /* type error if arrays truly passed by reference */ v can be used as an array reference, e.g. v[1] = 23 exactly as in the pass-by-reference fortran example. And v can also be used as a local variable and reassigned. If the first option was the only one, would you not say C was definitely pass-by-reference (for arrays)? If one could only use v as a pointer (i.e. access the argument array as *(v+1) = 23, then I would say that arrays are not passed at all, only pointers by-value. That both these options exist causes me to conclude that, for arrays, parameter passing can be viewed as either arrays by-reference or pointers by-value. I don't understand what relevance type checking has. Since you are choosing to use v as a pointer, one would not expect a type error, yes? I guess the case for pass-by-value would be a little stronger because one has to have passing a pointer by value anyway (since pointers are first-class datatypes) and that can be used to describe passing arrays (as you described). The difference is that the /callee/ function is different between the two cases. Also, notice that arrays in expressions turn into pointers in the same way, so function argument passing works the same way as assignment -- a hallmark of pass-by-value. Not in all expressions as you yourself mentioned: int a[10], *ap; sizeof a; sizeof ap; -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Thu, 08 Jan 2009 18:33:50 +, Mark Wooding wrote: [Steven's message hasn't reached my server, so I'll reply to it here. Sorry if this is confusing.] Aaron Brady castiro...@gmail.com wrote: On Jan 8, 1:45 am, Steven D'Aprano ste...@remove.this.cybersource.com.au wrote: On Wed, 07 Jan 2009 10:17:55 +, Mark Wooding wrote: The `they're just objects' model is very simple, but gets tied up in knots explaining things. The `it's all references' model is only a little more complicated, but explains everything. But it *over* explains, because it implies things that everybody knows about references in other languages that aren't true for Python. I addressed this elsewhere. Summary: `pass-by-reference' is a different thing to `all you manipulate are references': You know, I've written a fair bit of Python code over the years, and I've never manipulated a reference *once*. Ints, strings, floats, lists, tuples... but references? Never. I'm pretty sure that no other pure-Python coder has manipulated references either. They've manipulated objects. Whatever the VM does under the hood is another story. If you insist on talking about implementations, then at least get it right: in Python, like every other programming language, all you do is flip bits. It's *all* bit flipping. That's why we should try to keep the different layers of explanation separate, without conflating them. Python programmers don't actually flip bits, and neither do they manipulate references. Python programmers don't have access to bits, or references. What they have access to is objects. (Of course, there are ways to get under the hood if you really want to.) Python does pass-by-value, but the things it passes -- by value -- are references. If you're going to misuse pass-by-value to describe what Python does, *everything* is pass-by-value where the value is foo, for some foo. You can't have anything but pass-by-value with current computer technology, because computers don't actually move arguments, they only copy bytes. So pass-by-value becomes a meaningless term, because it describes every computer language imaginable, including hypothetical ones using calling conventions not yet invented, and therefore explains nothing. (The `pass-by-*' notions are confusingly named anyway. Pass-by-name doesn't actually involve names at all.) You might find them confusing, but I don't. What I find confusing is that people insist on misusing terminology invented for describing one type of behaviour in order to use it for a completely different type of behaviour just because of certain similarities under the hood. Of course it's not literally true that everybody knows that you can use references to implement a swap(x, y) procedure. But people coming from a C or Pascal background tend to assume that everything is like C/Pascal, and there are a lot of them. If C was a rare, unfamiliar language, my opposition to using the term reference would be a lot milder. Maybe in another five years? I agree with the comment about Pascal, but C is actually pretty similar to Python here. C only does pass-by-value. Except for arrays. If you want a function to modify your variable, you have to pass a pointer value which points to it. Yes, because the variable is copied before the function sees it. So if you pass a struct, and modify one of the struct's fields, the caller doesn't see the change. Now try that with Python, and you'll see completely different behaviour. (You'll have to use something *like* a struct, because Python doesn't have them. Try an object with attributes.) In other words... C is call-by-value, and (according to you) Python is call-by-value, but they behaviour differently. Python has no pointer values, so you need a different hack. The hack usually involves lists. (Though it's easier in the main to return compound data objects like tuples. I don't suppose that a proposal for true multiple return values would go down well here. No, didn't think so...) Out of curiosity, what makes Python returning tuples less true than true multiple return values, and what can you do with TMRVs that you can't do with tuples? Okay, the abstraction has leaked again... are the paperweights references to the objects, or the names we've bound objects to? I'm confused... They're the references to the objects. You don't bind names to objects. Amazing. It sure feels like it to me. x = 23 There's a name, and an object, and I've bound the name to the object so I can refer to the object 23 by the name x. You bind names slots in which you store references. I'm pretty sure I don't. I'd have noticed. You may have missed my last question: How do we deal with anonymous objects in your model? What I am pretty sure of is that references are going to have to enter the picture at some point, because other models get too complicated. Well, I dare
Re: why cannot assign to function call
On Jan 9, 4:01 am, Steven D'Aprano st...@remove-this- cybersource.com.au wrote: On Thu, 08 Jan 2009 18:33:50 +, Mark Wooding wrote: [Steven's message hasn't reached my server, so I'll reply to it here. Sorry if this is confusing.] Aaron Brady castiro...@gmail.com wrote: On Jan 8, 1:45 am, Steven D'Aprano ste...@remove.this.cybersource.com.au wrote: On Wed, 07 Jan 2009 10:17:55 +, Mark Wooding wrote: The `they're just objects' model is very simple, but gets tied up in knots explaining things. The `it's all references' model is only a little more complicated, but explains everything. But it *over* explains, because it implies things that everybody knows about references in other languages that aren't true for Python. I addressed this elsewhere. Summary: `pass-by-reference' is a different thing to `all you manipulate are references': You know, I've written a fair bit of Python code over the years, and I've never manipulated a reference *once*. Ints, strings, floats, lists, tuples... but references? Never. snip That's why we should try to keep the different layers of explanation separate, without conflating them. Python programmers don't actually flip bits, and neither do they manipulate references. Python programmers don't have access to bits, or references. What they have access to is objects. snip How do we deal with anonymous objects in your model? What I am pretty sure of is that references are going to have to enter the picture at some point, because other models get too complicated. Well, I dare say that at *some* point all models are insufficient. The map is not the territory, and there's always something that gets left out. But I think your model with strings is more complicated: robots, sticky Blu-Tack, string that you can't touch or see, and so forth. Compared to that, TARDIS technology enabling objects to be in two places at once is remarkably straightforward. Despite it being physically unrealistic, it's logically simple. -- Steven Possible compromise. You can think of functions as mutation-only. You pass the object, and it gets a new (additional) name. The old name doesn't go in. /compromise Regardless, IMO, references don't add any explanatory power; they just make you feel cocky that you know what they are. M: If 'fun()' returned a reference, you would be able to assign to it. m: You can't assign to it. C: It doesn't return a reference. -- Why can't I assign to a function call? -- Python variables are references only. -- Ok, why can't I assign to a function call? -- [Explanation Steven is trying to give.] In other words, Mark and Joe, cut to the chase. You're shirking. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
ru...@yahoo.com wrote: a = array (1,2,3) b = a a[1] = 4 print b C, C++, VBA, Fortran, Perl: 1, 2, 3 Python: 1, 4, 3 You are mistaken I don't think so. See http://groups.google.com/group/comp.lang.python/msg/f99d5a0d8f869b96 The code I quoted there was tested. In the C/C++ case, array-to-pointer coercion confuses the issue so I embedded the array in a struct which is more like an object. No, that's cheating (IMHO). Structs used directly (rather than via pointers) are the odd beast, and while they're certainly possible in C, they are far from the normal idiom. And certainly embedding an array in a struct just to force it to be copied, avoiding the standard reference semantics, is an arbitrary trick. I never claimed that you *couldn't* have copy semantics in C; you can do almost anything you want in C (or C++). But the *normal* usage of an array is via a pointer, in which case the semantics are exactly the same as in Python, Java, REALbasic, .NET, etc. (Keep in mind my point was not to show the behavior of arrays, but to show that several common languages *do not* use Python's *all* names are references model -- though of course this does not preclude their having some Python-like assignments since they all have some way of doing references.) Ah. OK then, I guess I missed you're point. You're absolutely right; many languages have both reference types and value types. Python is a bit unusual in that it has only reference types. I would have picked a different example to illustrate that, but it's true nonetheless. Best, - Joe -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Aaron Brady wrote: Possible compromise. You can think of functions as mutation-only. You pass the object, and it gets a new (additional) name. The old name doesn't go in. /compromise That's correct. The reference itself is passed in, not the variable (or expression) that held or generated the reference in the calling code. This is no different from, in C, passing an integer: void foo(int bar) { bar = 42; } int baz = 0; foo(baz); This doesn't change baz because, speaking precisely, baz wasn't passed to foo -- only the *value* of baz (i.e. 0) was passed to foo. Within foo, that value was stored in bar. Assigning a new value to bar does not affect foo. It's the exact same thing when the value happens to be a reference to an object: typedef SomeClass* SomeClassPtr; void foo(SomeClassPtr bar) { bar = new SomeClass(); } SomeClassPtr baz = NULL; foo(baz); Again, we're not passing baz into foo; we're passing the *value* of baz (i.e. NULL) into foo. That value is stored in bar within foo, and when we assign a new value (a reference to a freshly minted SomeClass object) into bar, of course it doesn't affect baz. Regardless, IMO, references don't add any explanatory power; they just make you feel cocky that you know what they are. Nonsense. They are the simple and clear explanation of what's going on. M: If 'fun()' returned a reference, you would be able to assign to it. m: You can't assign to it. C: It doesn't return a reference. C is false because M is false (at least, in the way I believe you mean it). Or, more precisely, both M and m are nonsensical; what does it mean to assign to a reference? It makes no sense. What would it mean to assign to 42? You don't assign to references any more than you assign to integers or assign to strings or assign to None. Those are all values, and you don't assign to values -- you assign to variables. Assign means to give a new value to a variable, i.e. to let (or cause) a variable to have a new value. (Ye olde BASIC even used the LET keyword to indicate this, e.g. LET X = 42.) Speaking casually, we don't always make this distinction, but I think precision is needed here. So, I see two ways to make sense of your argument: M1: If 'fun()' returned a variable, you would be able to assign to it. m1: You can't assign to it. C1: It doesn't return a variable. This is true. (Technically, instead of variable, we should say LValue here -- there are things slightly more complex than simple variables that can serve as the left-hand side of an assignment. So replace variable with lvalue above if you prefer.) Or, the other way some may see it is: M2: If 'fun()' returned a reference, you might be able to mutate the object that refers to. m2: You can sometimes mutate the object it refers to. C2: 'fun()' returns a reference. This is true (though the logic is flawed, but fixable). -- Why can't I assign to a function call? -- Python variables are references only. -- Ok, why can't I assign to a function call? You're right, Python variables are references only has nothing to do with it. In a language where the only data type were integer, you wouldn't be able to assign to a function call either. You can't assign to a function call because a function call is not itself an lvalue, and it doesn't return an lvalue. That's just not something you can get for free from the type model; it's an extra feature that would have to be built into the language somehow, and Python doesn't have it. If assignment were an operator rather than a statement, and could be overridden in a class via some specially-named method -- or, for that matter, if the assignment statement looked for such a method when it finds an object reference on the left-hand side -- then any object could be an lvalue, and you COULD (in some cases) assign to the result of a function. But it's not and it doesn't. Best, - Joe -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
ru...@yahoo.com ru...@yahoo.com wrote: As a side comment (because it always bugs me when I read this, even though I read it in very authoritative sources), ISTM that C passes everything by value except arrays; they are passed by reference (by passing a pointer to the array by value.) Admittedly, the close relationship between arrays and pointers makes it easy conflate them. Arrays are distinctly second-class citizens in C. Basically, in C, arrays aren't passed at all but there's some syntactic sugar so you can squint and con yourself that they're passed by reference. If you try to pass an array, the array name immediately decays to a pointer, and the pointer gets passed instead -- by value. The corresponding function parameter must be a pointer to an approrpriate kind of thing, though you're allowed to write []s to confuse yourself if you like -- T D[] in a function parameter declaration means precisely the same as T *D -- to the extent that D has type T **D and so on. -- [mdw] -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Joe Strout wrote: Aaron Brady wrote: Possible compromise. You can think of functions as mutation-only. You pass the object, and it gets a new (additional) name. The old name doesn't go in. /compromise That's correct. The reference itself is passed in, not the variable (or expression) that held or generated the reference in the calling code. This is no different from, in C, passing an integer: void foo(int bar) { bar = 42; } int baz = 0; foo(baz); This doesn't change baz because, speaking precisely, baz wasn't passed to foo -- only the *value* of baz (i.e. 0) was passed to foo. Within foo, that value was stored in bar. Assigning a new value to bar does not affect foo. It's the exact same thing when the value happens to be a reference to an object: typedef SomeClass* SomeClassPtr; void foo(SomeClassPtr bar) { bar = new SomeClass(); } SomeClassPtr baz = NULL; foo(baz); Again, we're not passing baz into foo; we're passing the *value* of baz (i.e. NULL) into foo. That value is stored in bar within foo, and when we assign a new value (a reference to a freshly minted SomeClass object) into bar, of course it doesn't affect baz. Regardless, IMO, references don't add any explanatory power; they just make you feel cocky that you know what they are. Nonsense. They are the simple and clear explanation of what's going on. M: If 'fun()' returned a reference, you would be able to assign to it. m: You can't assign to it. C: It doesn't return a reference. C is false because M is false (at least, in the way I believe you mean it). Or, more precisely, both M and m are nonsensical; what does it mean to assign to a reference? It makes no sense. What would it mean to assign to 42? You don't assign to references any more than you assign to integers or assign to strings or assign to None. Those are all values, and you don't assign to values -- you assign to variables. Assign means to give a new value to a variable, i.e. to let (or cause) a variable to have a new value. (Ye olde BASIC even used the LET keyword to indicate this, e.g. LET X = 42.) Speaking casually, we don't always make this distinction, but I think precision is needed here. So, I see two ways to make sense of your argument: M1: If 'fun()' returned a variable, you would be able to assign to it. m1: You can't assign to it. C1: It doesn't return a variable. This is true. (Technically, instead of variable, we should say LValue here -- there are things slightly more complex than simple variables that can serve as the left-hand side of an assignment. So replace variable with lvalue above if you prefer.) Or, the other way some may see it is: M2: If 'fun()' returned a reference, you might be able to mutate the object that refers to. m2: You can sometimes mutate the object it refers to. C2: 'fun()' returns a reference. This is true (though the logic is flawed, but fixable). -- Why can't I assign to a function call? -- Python variables are references only. -- Ok, why can't I assign to a function call? You're right, Python variables are references only has nothing to do with it. In a language where the only data type were integer, you wouldn't be able to assign to a function call either. You can't assign to a function call because a function call is not itself an lvalue, and it doesn't return an lvalue. That's just not something you can get for free from the type model; it's an extra feature that would have to be built into the language somehow, and Python doesn't have it. If assignment were an operator rather than a statement, and could be overridden in a class via some specially-named method -- or, for that matter, if the assignment statement looked for such a method when it finds an object reference on the left-hand side -- then any object could be an lvalue, and you COULD (in some cases) assign to the result of a function. But it's not and it doesn't. Pretty soon you guys will have me believing Python doesn't work ... regards Steve -- Steve Holden+1 571 484 6266 +1 800 494 3119 Holden Web LLC http://www.holdenweb.com/ -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Fri, 09 Jan 2009 08:30:46 -0700, Joe Strout wrote: That's correct. The reference itself is passed in, not the variable (or expression) that held or generated the reference in the calling code. This is no different from, in C, passing an integer: void foo(int bar) { bar = 42; } int baz = 0; foo(baz); This doesn't change baz because, speaking precisely, baz wasn't passed to foo -- only the *value* of baz (i.e. 0) was passed to foo. We can check whether bar and baz are the same thing, by printing their addresses: #includestdio.h void foo(int bar) { printf(\n The address of bar is %p , bar); bar = 42; } int main() { int baz = 0; printf(\n The address of baz is %p , baz); foo(baz); return 0; } When I do this, I get: The address of baz is 0xbfa72870 The address of bar is 0xbfa72850 So we can agree that baz and bar are different entities. Now, would anyone like to take a small wager on what the equivalent code in CPython would print? def foo(bar): print The address of bar is %s % id(bar) bar = 42 baz = 0 print The address of baz is %s % id(baz) foo(baz) When I execute this, I get The address of baz is 143599468 The address of bar is 143599468 Python doesn't do the same thing as C. It actually passes the same value to the function, without copying it. Why oh why do you keep insisting that Python is no different from C? Let's try one more. In C: #include stdio.h #include stdlib.h struct record { int x; }; void mutate(struct record bar) { printf(\n The address of bar is %p , bar); bar.x = 0; printf(\n Inside: %d , bar.x); } struct record baz; int main(void) { baz.x = 1; printf(\n The address of baz is %p , baz); printf(\n Before: %d , baz.x); mutate(baz); printf(\n After: %d , baz.x); return 0; } gives output: The address of baz is 0x80496fc Before: 1 The address of bar is 0xbfb8f980 Inside: 0 After: 1 We can clearly see that baz and bar are different entities, and changes made to bar inside the function don't affect baz. A Python equivalent: class struct: pass def mutate(bar): print The address of bar is %d % id(bar) bar.x = 0; print Inside: %d % bar.x baz = struct() baz.x = 1 print The address of baz is %d % id(baz) print Before: %d % baz.x mutate(baz) print After: %d % baz.x which gives output: The address of baz is 3085537420 Before: 1 The address of bar is 3085537420 Inside: 0 After: 0 Why oh why do you keep insisting that Python is no different from C? -- Steven -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Joe Strout wrote: ru...@yahoo.com wrote: a = array (1,2,3) b = a a[1] = 4 print b C, C++, VBA, Fortran, Perl: 1, 2, 3 Python: 1, 4, 3 You are mistaken I don't think so. See http://groups.google.com/group/comp.lang.python/msg/f99d5a0d8f869b96 The code I quoted there was tested. In the C/C++ case, array-to-pointer coercion confuses the issue so I embedded the array in a struct which is more like an object. No, that's cheating (IMHO). Structs used directly (rather than via pointers) are the odd beast, and while they're certainly possible in C, they are far from the normal idiom. And certainly embedding an array in a struct just to force it to be copied, avoiding the standard reference semantics, is an arbitrary trick. It was intended as a purely rhetorical device to avoid requiring a lot of verbiage to say why a struct example is used for C and arrays for the others and why that makes no difference to my point. Actually, the whole code/results thing was purely for effect since I presume most people reading already understand how assignments work in these languages, or that least that there are significant languages using copy-like assignments. I never claimed that you *couldn't* have copy semantics in C; you can do almost anything you want in C (or C++). But the *normal* usage of an array is via a pointer, in which case the semantics are exactly the same as in Python, Java, REALbasic, .NET, etc. Arrays are the only datatype in C that don't use copy-like assignment. Everything else does. (Keep in mind my point was not to show the behavior of arrays, but to show that several common languages *do not* use Python's *all* names are references model -- though of course this does not preclude their having some Python-like assignments since they all have some way of doing references.) Ah. OK then, I guess I missed you're point. You're absolutely right; many languages have both reference types and value types. Python is a bit unusual in that it has only reference types. I would have picked a different example to illustrate that, but it's true nonetheless. If one accepts that there are a lot of people who post in here that clearly are surprised by Python's assignment semantics, and further appear to expect assignment to have copy-like semantics, then where is that expectation coming from? How would anyone develop that expectation if (from a different post in this thread), [Python's] idea of assignment is the same as anyone else's. If you maintain that reference-like assignment is very common and something every programmer is accustomed to, then where are they getting the copy-like assignment expectations from? I agree that most of the time, when one is using large (memory) composite objects, and one needs to pass, or access them by different names, one will often use references to do so in order to avoid expensive copies or to get desired shared behavior. But (with the exception of C arrays [*1]), doing so requires some special syntax in all the languages I mentioned (AFAIK). So it still seems to me that this is a likely explanation to why there is frequent misunderstanding of Python's assignments, and why responding to such misunderstandings with, Python's assignments are the same as other languages', is at best not helpful. [*1] I have often wished that C handled arrays the same way it does structs. I am sure that the pointer-array pseudo-equivalence seemed like a very clever idea at the time but I wonder if Dennis Richie ever had second thoughts about it. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
ru...@yahoo.com wrote: I never claimed that you *couldn't* have copy semantics in C; you can do almost anything you want in C (or C++). But the *normal* usage of an array is via a pointer, in which case the semantics are exactly the same as in Python, Java, REALbasic, .NET, etc. Arrays are the only datatype in C that don't use copy-like assignment. Everything else does. No, arrays are just one reference type; pointers are another (and in most ways, these are the same thing). When dealing with objects in C++, one routinely handles them with pointers, so that's the use case which is analogous to Python -- all the value types can be ignored for the sake of comparison. (C is not an OOP language, of course, but even there, all but the most trivial of structs are usually allocated on the heap and passed around via pointers, just like in C++, Java, .NET, RB, and Python.) Ah. OK then, I guess I missed you're point. You're absolutely right; many languages have both reference types and value types. Python is a bit unusual in that it has only reference types. I would have picked a different example to illustrate that, but it's true nonetheless. If one accepts that there are a lot of people who post in here that clearly are surprised by Python's assignment semantics, and further appear to expect assignment to have copy-like semantics, then where is that expectation coming from? I think it comes from people stumbling across posts in this forum claiming that Python has unusual assignment semantics. I wish people would stop saying that, as it causes a lot of confusion. How would anyone develop that expectation if (from a different post in this thread), [Python's] idea of assignment is the same as anyone else's. I can think of two ways: 1. They're new to programming in general, and would have had the same expectation for any other language. OR, 2. They already understand some other language, and then they come here and read wild claims that Python's assignment and parameter-passing semantics are different from other languages. Duped by this claim, they conclude that, if it's unlike other languages, then Python must have copy semantics. If you maintain that reference-like assignment is very common and something every programmer is accustomed to, then where are they getting the copy-like assignment expectations from? Reference-like assignment IS very common (see http://www.strout.net/info/coding/valref/). So, see above. I agree that most of the time, when one is using large (memory) composite objects, and one needs to pass, or access them by different names, one will often use references to do so in order to avoid expensive copies or to get desired shared behavior. Right. But (with the exception of C arrays [*1]), doing so requires some special syntax in all the languages I mentioned (AFAIK). Whether you consider it special or not, pointers are extremely common in C. Even more so in C++, which is the closest thing to an OOP language in the list of moldy languages you mentioned. You also mentioned VBA -- if that's anything like VB, it does NOT require any special syntax; a variable is a reference type if its declared type is a class or string, and a simple type if it's anything else (just like in .NET, Java, and REALbasic). So it still seems to me that this is a likely explanation to why there is frequent misunderstanding of Python's assignments, and why responding to such misunderstandings with, Python's assignments are the same as other languages', is at best not helpful. I don't think so. More likely, people are being confused by the claim that Python's assignments are NOT like other languages, when in fact they are. Best, - Joe -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
[Sigh. I must apologize for the length of this article. I can't, alas, see a satisfactory way of trimming it. The doubly-quoted stuff later on was by me.] Steven D'Aprano st...@remove-this-cybersource.com.au wrote: I'm pretty sure that no other pure-Python coder has manipulated references either. They've manipulated objects. No: not directly. The Python program deals solely with references; anything involving actual objects is mediated by the runtime. Whatever the VM does under the hood is another story. (And, to stave off a return to the discussion about synchronized clones and other implementation techniques, whether a reference is a memory pointer, a tagged immutable immediate value, or an element of an equivalence-class of clones is an irrelevant implementation detail; but the reference needs to exist to explain observed, specified behaviour. For the purposes of this discussion, I shall continue to use the commonly accepted term `reference' to denote an arbitrary and implementation-specific choice of the isomorphism class of such possible implementation techniques.) That's why we should try to keep the different layers of explanation separate, without conflating them. Python programmers don't actually flip bits, and neither do they manipulate references. Python programmers don't have access to bits, or references. What they have access to is objects. No, that's my point: Python programmers /don't/ have direct access to objects. The objects themselves are kept at arm's length by the indirection layer of references. (Of course, there are ways to get under the hood if you really want to.) Yes, but -- as I've done throughout this discussion -- I shall continue to use only Python examples whose behaviour is fully specified and implementation independent in order to support my thesis. Python does pass-by-value, but the things it passes -- by value -- are references. If you're going to misuse pass-by-value to describe what Python does, *everything* is pass-by-value where the value is foo, for some foo. No. I've tried explaining this before, with apparently little success. This is probably my fault: I don't seem to be good at explaining concepts to people whose mindset is significantly different to mine. (My refusal to ignore complicated corner cases doesn't help.) The words `value' and `reference' have significantly different meanings depending on whether we're talking on the one hand about data models and on the other hand about argument-passing models. In particular, the latter focuses on details at a lower abstraction level. This is extremely unfortunate, and is causing a lot of confusion. I can only speculate that the origins of this mess are historical and have their origins in the fact that lower-level languages have tended to have more exotic argument-passing models. This is going to be (as far as I can make it) a language-independent survey. That in itself is going to make matters complicated, because I know a lot of programming languages, and they differ in sometimes subtle ways. Enough of the disclaimers, and on to some terminology. For the avoidance of confusion, I shall use the following terms in perhaps technical senses, defined below. Even so, I believe that I'm using these terms in the senses (or at least, in ways similar to the senses) commonly understood in the field of programming language design. * A /value/ is an item of data. The range and nature of values is language specific. Typically, values encompass at least some kinds of numbers, textual data, and compound data structures; they may also include behavioural items such as functions. * A /location/[1] is an area of memory suitable for storing the /immediate representation/ (which I shall abbreviate to /IR/) of a value. (A location may be capable of storing things other than IRs, e.g., representations of unevaluated expressions in lazily evaluated languages. Locations may vary in size, e.g., in order to be capable of storing different types of IRs.) * A /variable/ is a location to which has been /bound/ a name. Given an occurrence of a name in a program's source, there is a language specific rule for determining the variable to which it is bound. * /Evaluation/ is the process of determining a value from an expression. The /value of/ an expression is the result of evaluating the expression. This value is, in general, dependent on the contents of the locations to which names appearing in the expression are bound. * A /function/ (synonymously, /procedure/) is a subprogram which may be /called/ by another (not necessarily distinct) part of the program, supplying zero or more /arguments/, performing a computation depending on these arguments, and returning zero or more /results/. Whether functions are values is language specific. The nature of the arguments and results is language specific.
Re: why cannot assign to function call
Aaron Brady castiro...@gmail.com wrote: Possible compromise. You can think of functions as mutation-only. You pass the object, and it gets a new (additional) name. The old name doesn't go in. /compromise Huh? That doesn't explain circular data structures at all, unless your idea of `name' is unrelated to the identifiers the programmer has typed and in fact is the same as what I'm calling a reference. Regardless, IMO, references don't add any explanatory power; they just make you feel cocky that you know what they are. No, they're essential to understanding sharing and circular data. M: If 'fun()' returned a reference, you would be able to assign to it. [mdw]: Why would you think that you can assign to a reference? You assign to variables, not references. -- Why can't I assign to a function call? -- Python variables are references only. You may have missed this, but I explained at least twice: variables are bound to `slots' or `locations'; locations, in Python, store references to values; references are not values. But I answered this lot ages ago. I'm still dealing with the fallout because of my approach, though. In other words, Mark and Joe, cut to the chase. You're shirking. I'm trying to explain somewhat complex concepts in defence of my thesis. This is not intended to be an answer to the original poster: the discussion has become considerably more technical since then. -- [mdw] -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Steven D'Aprano st...@remove-this-cybersource.com.au wrote: Python doesn't do the same thing as C. It actually passes the same value to the function, without copying it. Why oh why do you keep insisting that Python is no different from C? I'm beginning to think that you're not bothing to read what I'm writing, but I'll assume instead that I'm just not writing clearly enough. In one respect, C and Python are alike. That respect is that they both pass arguments to functions `by value'. In another respect, C and Python are different. That respect is that they represent these arguments differently: C stores a binary representation of the value directly in the object allocated to store it (6.2.6.1); whereas Python stores a reference to the actual value (3.1, 4.1). This is what I mean when I say that Python passes arguments by value -- but the `values' that are passed are references. References to the C standard are to ISO 9899:1999, though I'm fairly sure that N843 and N1256 will do as well; references to the Python Language Reference are to the 2.5.2 version dated 2008-02-21. I'll note here that the PLM uses the term `binding' in the usual sense understood by the larger programming-language community, and not as a confusing synonym for assignment. Why oh why do you keep insisting that Python is no different from C? Why oh why do you keep insisting that, because I claim that the two languages are alike in one respect, that I believe that they must be alike in a different respect, despite my repeated assertions to the contrary? -- [mdw] -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
ru...@yahoo.com ru...@yahoo.com wrote: If one accepts that there are a lot of people who post in here that clearly are surprised by Python's assignment semantics, But one should not accept that. One might accept that there are many who post who claim that they are surprised by Python's assignment semantics. Such people are wrong, however, since what they are surprised by is Python's data model, and one reason that they are surprised by Python's data model is because it's not actually explained very well. and further appear to expect assignment to have copy-like semantics, This expectation is justified and, indeed, satisfied. Python does, most definitely, copy on assignment. What it copies is references, however. This might be clearer if the data model were explained better. As an aside, I don't notice anywhere near as much confusion in Lisp and Scheme groups, which might be surprising since Lisp and Scheme have precisely the same data model, argument passing convention, and assignment semantics, as Python has. There are many possible explanations: * The Lisp and Scheme communities are smaller. This is certainly true. But it wouldn't explain what appears to be a disproportionate level of confusion on the topic among Python beginners. * Individuals in the Lisp and Scheme communities are cleverer and/or more widely experienced. One might make an argument that this is true and a result of the relative community sizes -- basically a result of self-selection. But instead I'll reject this as an explanation. It's arrogant and unproven. * The Lisp and Scheme communities make a concerted effort to explain their data model clearly and precisely. They accept that it's actually quite complicated and, rather than pretend that it isn't, explain the complexity and the benefits it brings that make the complexity worthwhile. I think this is the likely one. then where is that expectation coming from? How would anyone develop that expectation if (from a different post in this thread), [Python's] idea of assignment is the same as anyone else's. Because they've fundamentally misunderstood the data model. The very fact that their confusion is ascribed to the wrong thing is strongly indicative of this. If you maintain that reference-like assignment is very common and something every programmer is accustomed to, then where are they getting the copy-like assignment expectations from? But it's not just assignment that deals with references. It's argument passing and storage of compound data as well. (See PLR 3.1.) They expect that assignment copies stuff, because that's what assignment does. Everywhere that I can think of -- except C++, which leaves assignment semantics in hands of the programmer. What they're confused about is what, precisely, it is that gets copied. And that, really, is a result of an inadequate understanding of the data model. I agree that most of the time, when one is using large (memory) composite objects, and one needs to pass, or access them by different names, one will often use references to do so in order to avoid expensive copies or to get desired shared behavior. But (with the exception of C arrays [*1]), doing so requires some special syntax in all the languages I mentioned (AFAIK). Ummm... you mentioned C, C++, `Python, Java, REALbasic, .NET'. Well, C we've dealt with. C++ is weird. Python we all know, and is the main subject of the argument. REALbasic I don't know at all, but BASICs traditionally represent data fairly directly (rather than via references) so will largely be like C. .NET isn't a language at all: rather, it's a virtual machine, runtime system, class library and family of languages each of which may have idiosyncratic semantics. Which leaves Java. Java divides the world into `primitive' and `reference' types (4.1). The former are represented directly; the latter have a pointer to the true data as immediate representation. But observe that Java's primitive types are integer types (including its misnamed `char'), floating-point types, and booleans. Equivalent objects of all of these are immutable in Python -- and there is no observable difference (unless exposed by an operator like `is' or the `id' built-in) between a directly represented object and an /immutable/ object represented by reference. (I've no doubt that the original language designers were aware of this equivalence, so it's a mystery to me precisely why they specified the language as they did rather than leaving the messy business of boxing and unboxing primitive values to the compiler, which, given Java's mandatory type annotations, should have found the job trivial.) So it still seems to me that this is a likely explanation to why there is frequent misunderstanding of Python's assignments, and why responding to such misunderstandings with, Python's assignments are the same as other languages', is at best not
Re: why cannot assign to function call
Mark Wooding wrote: As an aside, I don't notice anywhere near as much confusion in Lisp and Scheme groups, which might be surprising since Lisp and Scheme have precisely the same data model, argument passing convention, and assignment semantics, as Python has. Nor is there anywhere near as much confusion in the REALbasic community (with which I'm most familiar), which also has the same semantics for reference types (which in RB is everything except numbers, colors, and Boolean). Apparently there is occasionally a little confusion in the Java community, but it rarely reaches the sillyness proportions seen here: http://javadude.com/articles/passbyvalue.htm * The Lisp and Scheme communities are smaller. This is certainly true. But it wouldn't explain what appears to be a disproportionate level of confusion on the topic among Python beginners. * Individuals in the Lisp and Scheme communities are cleverer and/or more widely experienced. One might make an argument that this is true and a result of the relative community sizes -- basically a result of self-selection. But instead I'll reject this as an explanation. It's arrogant and unproven. * The Lisp and Scheme communities make a concerted effort to explain their data model clearly and precisely. They accept that it's actually quite complicated and, rather than pretend that it isn't, explain the complexity and the benefits it brings that make the complexity worthwhile. I think this is the likely one. That's a nice way of putting it. I might go a step further and say that there is a small but vocal portion of the Python community that insists on confounding the issue by claiming that Python's assignment and parameter-passing conventions are different from other languages. (Which, of course, requires one's head to be firmly in the sand with regard to the basic fact that Python variables contain references, not objects.) But it's not just assignment that deals with references. It's argument passing and storage of compound data as well. (See PLR 3.1.) They expect that assignment copies stuff, because that's what assignment does. Everywhere that I can think of -- except C++, which leaves assignment semantics in hands of the programmer. What they're confused about is what, precisely, it is that gets copied. And that, really, is a result of an inadequate understanding of the data model. I have nothing to add to this. It just seem well worth quoting. :) I agree that most of the time, when one is using large (memory) composite objects, and one needs to pass, or access them by different names, one will often use references to do so in order to avoid expensive copies or to get desired shared behavior. But (with the exception of C arrays [*1]), doing so requires some special syntax in all the languages I mentioned (AFAIK). Ummm... you mentioned C, C++, `Python, Java, REALbasic, .NET'. No, actually, that was me. rurpy's list was something like C, FORTRAN, Perl, and VBA. Well, C we've dealt with. C++ is weird. Python we all know, and is the main subject of the argument. REALbasic I don't know at all, but BASICs traditionally represent data fairly directly (rather than via references) so will largely be like C. Not REALbasic. It's a very modern language with semantics pretty much identical to Java. Simple types (numbers, colors, Booleans) are stored directly; all other types (including strings, objects, and arrays) are stored on the heap and accessed via references. .NET isn't a language at all: rather, it's a virtual machine, runtime system, class library and family of languages each of which may have idiosyncratic semantics. But they don't, AFAIK -- they all have the same semantics; only the surface syntax differs. And those semantics are the same as REALbasic and Java. See http://www.strout.net/info/coding/valref/ for some side-by-side comparisons. Which leaves Java. Java divides the world into `primitive' and `reference' types (4.1). The former are represented directly; the latter have a pointer to the true data as immediate representation. Right -- a very common pattern among modern languages. So it still seems to me that this is a likely explanation to why there is frequent misunderstanding of Python's assignments, and why responding to such misunderstandings with, Python's assignments are the same as other languages', is at best not helpful. That's why I'm not just saying that assignment is the same. I'm also saying that the data model is most definitely not the same as C. Technically true, in that pointers in C require some special syntax, but the common idiom is to hide this away by defining a new type: typedef Foo* FooPtr; Now, for any code using the FooPtr type, the data model is the same as Python (or as Java, RB, .NET, etc., again for code that's using only reference types). Best, - Joe --
Re: why cannot assign to function call
Joe Strout wrote: ru...@yahoo.com wrote: I never claimed that you *couldn't* have copy semantics in C; you can do almost anything you want in C (or C++). But the *normal* usage of an array is via a pointer, in which case the semantics are exactly the same as in Python, Java, REALbasic, .NET, etc. Arrays are the only datatype in C that don't use copy-like assignment. Everything else does. No, arrays are just one reference type; pointers are another (and in most ways, these are the same thing). Pointers are passed and assigned by value, just as other types (disputedly except arrays) are. One can then use that pointer to manually effect pass-(the-value-pointed-to)-by-reference, or sharing, etc. When dealing with objects in C++, one routinely handles them with pointers, so that's the use case which is analogous to Python -- all the value types can be ignored for the sake of comparison. (C is not an OOP language, of course, but even there, all but the most trivial of structs are usually allocated on the heap and passed around via pointers, just like in C++, Java, .NET, RB, and Python.) In C (you have to explicitly ask for a reference (pointer) to something (other than arrays) if you want to use/pass a reference to something. If you simply use the name, you get by-value semantics. Ah. OK then, I guess I missed you're point. You're absolutely right; many languages have both reference types and value types. Python is a bit unusual in that it has only reference types. I would have picked a different example to illustrate that, but it's true nonetheless. If one accepts that there are a lot of people who post in here that clearly are surprised by Python's assignment semantics, and further appear to expect assignment to have copy-like semantics, then where is that expectation coming from? I think it comes from people stumbling across posts in this forum claiming that Python has unusual assignment semantics. I wish people would stop saying that, as it causes a lot of confusion. How would anyone develop that expectation if (from a different post in this thread), [Python's] idea of assignment is the same as anyone else's. I can think of two ways: 1. They're new to programming in general, and would have had the same expectation for any other language. OR, IIRC, Someone posted here that his experience was that 12-year old kids (presumably without programming experience) had no problem with Python and references when described as names given to an object. (From memory, can't locate the post right now.) 2. They already understand some other language, and then they come here and read wild claims that Python's assignment and parameter-passing semantics are different from other languages. Duped by this claim, they conclude that, if it's unlike other languages, then Python must have copy semantics. I have seen no evidence of that. If it were true I would expect at least some posts to refer to reading those wild claims. If you maintain that reference-like assignment is very common and something every programmer is accustomed to, then where are they getting the copy-like assignment expectations from? Reference-like assignment IS very common (see http://www.strout.net/info/coding/valref/). So, see above. I agree that most of the time, when one is using large (memory) composite objects, and one needs to pass, or access them by different names, one will often use references to do so in order to avoid expensive copies or to get desired shared behavior. Right. But (with the exception of C arrays [*1]), doing so requires some special syntax in all the languages I mentioned (AFAIK). Whether you consider it special or not, pointers are extremely common in C. Even more so in C++, which is the closest thing to an OOP language in the list of moldy languages you mentioned. Non-special is b = a. You also mentioned VBA -- if that's anything like VB, it does NOT require any special syntax; a variable is a reference type if its declared type is a class or string, and a simple type if it's anything else (just like in .NET, Java, and REALbasic). It (mercifully) been a long time since I've used VB but the VB code I posted does run, and does exhibit by-value assignment behavior. My faint recollection is that if you want to assign a reference you cannot write, b = a but must instead write set b = a. b = a assigns by value. In Perl it is definitely true that you different syntax: @a = (1,2,3) @b = @a # Copy $b = \...@a # Reference C is the same way for everything (including pointers) except arrays: struct {...} foo; foo a, b *bp; b = a; # Copy bp = a; # Reference So it still seems to me that this is a likely explanation to why there is frequent misunderstanding of Python's assignments, and why responding to such misunderstandings with, Python's assignments are the same as other languages', is at best not helpful. I don't think so. More
Re: why cannot assign to function call
On Fri, Jan 9, 2009 at 7:11 PM, Joe Strout j...@strout.net wrote: Mark Wooding wrote: .NET isn't a language at all: rather, it's a virtual machine, runtime system, class library and family of languages each of which may have idiosyncratic semantics. But they don't, AFAIK -- they all have the same semantics; only the surface syntax differs. And those semantics are the same as REALbasic and Java. They do in fact differ. You can have Visual C++ compile to CLI, in which case you have the C++ assignment in .NET, you can use VisualBasic, in which you can specify pass-by-value or -reference when you pass arguments to methods, and you have Visual C#, which copies Java's model and is therefore the same as Python. See http://www.strout.net/info/coding/valref/ for some side-by-side comparisons. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Fri, 09 Jan 2009 21:03:39 +, Mark Wooding wrote: Steven D'Aprano st...@remove-this-cybersource.com.au wrote: Python doesn't do the same thing as C. It actually passes the same value to the function, without copying it. Why oh why do you keep insisting that Python is no different from C? I'm beginning to think that you're not bothing to read what I'm writing, Er, perhaps you missed that I was replying to Joe Strout. Joe wrote: The reference itself is passed in, not the variable (or expression) that held or generated the reference in the calling code. This is no different from, in C, passing an integer: I'm pretty sure I clearly quoted Joe in my post. Perhaps you missed it. In any case, I'm happy for you to contribute (it's a public forum), but how do you conclude I'm not reading *your* posts because I disagree with Joe's claim that Python is just like C? -- Steven -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On 2009-01-10, Joe Strout j...@strout.net wrote: Mark Wooding wrote: As an aside, I don't notice anywhere near as much confusion in Lisp and Scheme groups, which might be surprising since Lisp and Scheme have precisely the same data model, argument passing convention, and assignment semantics, as Python has. Nor is there anywhere near as much confusion in the REALbasic community (with which I'm most familiar), which also has the same semantics for reference types (which in RB is everything except numbers, colors, and Boolean). It's not that there's a lot of confusion, it's just that we spend a lot of time talking about it. Programming in Python is so much more productive that we've got a lot more spare time that people who use other languages. -- Grant -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Sat, 10 Jan 2009 00:21:27 -, ru...@yahoo.com wrote: Joe Strout wrote: ru...@yahoo.com wrote: [snip] Pointers are passed and assigned by value, just as other types (disputedly except arrays) are. One can then use that pointer to manually effect pass-(the-value-pointed-to)-by-reference, or sharing, etc. [snip] In C (you have to explicitly ask for a reference (pointer) to something (other than arrays) if you want to use/pass a reference to something. If you simply use the name, you get by-value semantics. How would anyone develop that expectation if (from a different post in this thread), [Python's] idea of assignment is the same as anyone else's. I can think of two ways: 1. They're new to programming in general, and would have had the same expectation for any other language. OR, IIRC, Someone posted here that his experience was that 12-year old kids (presumably without programming experience) had no problem with Python and references when described as names given to an object. (From memory, can't locate the post right now.) 'Twas I. It was a rebuttal to your point that Python's assignment, parameter passing and data model is somehow inherently more difficult to wrap your brain around than that of other languages. It isn't; if anything it seems to be easier. 2. They already understand some other language, and then they come here and read wild claims that Python's assignment and parameter-passing semantics are different from other languages. Duped by this claim, they conclude that, if it's unlike other languages, then Python must have copy semantics. I have seen no evidence of that. If it were true I would expect at least some posts to refer to reading those wild claims. 3. They conflate assignment, parameter passing and the data model, bring in preconceptions of their own from other languages, and get caught out by them. It's quite easy to do, even within a language. Look at the number of times you had to say except arrays about C above. In Perl it is definitely true that you different syntax: @a = (1,2,3) @b = @a # Copy $b = \...@a # Reference Perl has different syntax for everything. It also has its own peculiarities of assignment and data model that make it a less than glowing example of comprehensibility. List flattening, for instance, is amazingly useful in many common Perl idioms, but really not what you expect during assignment. C is the same way for everything (including pointers) except arrays: Oh look, there's that exception again. It may be one can make a technical case that assignment is the same, but the reason I posted the code and results, was because the behavior of = in them produces clearly different results that = in Python. If you want to then say to someone, ignore those different results, '=' works exactly the same, all I can say is good luck. I will look forward to continuing to see weekly questions on this list. :-) It would help if you were using = on things that were conceptually the same across the languages, instead of things that only looked similar. Which is where the data model comes in, as has already been explained at length, several times now. -- Rhodri James *-* Wildebeeste Herder to the Masses -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Joe Strout j...@strout.net wrote: No, actually, that was me. rurpy's list was something like C, FORTRAN, Perl, and VBA. My mistake -- I failed to read the quoting properly. Apologies to all. We still dealt with C. Fortran (to give it its modern spelling) has a similar data model to C, but passes arguments by reference, as described in my Epic Argument Passing Article; I believe that arbitrary expressions may be used as arguments, though I'm unsure as to the semantics of modifying parameters bound to the resulting temporary argument locations. I must confess to being ignorant of VBA. My excuse is that I avoid Windows systems as much as practical, and VBA doesn't have a significant uptake on other systems. Perl has a very strange data model indeed, and it's hard to get a proper handle on it without getting into implementation details: unlike Python, Perl is largely defined /by/ its implementation. Perl has `references', which are proper (scalar) values through which one may read and modify other values; i.e., they're what I called `locatives' elsewhere. Perl provides syntactic sugar, through its `prototypes' which will convert an actual argument which designates (e.g.) a list or hash into a reference to that list or hash; prototypes provide other syntactic shortcuts too, though they have no fundamental semantic effect. In order to add to the confusion, Perl also provides `typeglobs', which are a reification of toplevel variable bindings. Perl's argument passing is fundamentally by /reference/. Given the function sub swap { ($_[0], $_[1]) = ($_[1], $_[0]) } after setting $a = 1, $b = 2, and calling swap $a, $b, we find that $a has the value 2 and $b is 1. What's going on here is that a `location' in Perl is an explicit SV, AV or HV object (for `scalar-', `array-' and `hash-value' respectively. Calling a subroutine involves marking a position on a stack, pushing a number of SVs, and then executing the subroutine's code, which receives the items between the stack pointer and mark in the @_ array. In the case of argument expressions which designate SVs, those SVs are pushed directly, and are therefore made available via @_. Arguments which are arrays or hashes are flattened: their components are pushed onto the stack. (This use of the stack corresponds to what the Perl manual refers to as `list context'.) Not REALbasic. It's a very modern language with semantics pretty much identical to Java. Very well; thanks for the correction. I might have a look at this at some point. .NET isn't a language at all: rather, it's a virtual machine, runtime system, class library and family of languages each of which may have idiosyncratic semantics. But they don't, AFAIK -- they all have the same semantics; only the surface syntax differs. And those semantics are the same as REALbasic and Java. There's a .NET implementation of C++, which obviously brings all of C++'s low-level data model (and it's user-definable assignment and copying). C#'s data model is more complex than Java's because it provides mutable compound `value types', i.e., types whose immediate representations consist of the raw contents of the object rather than a reference. The mutability allows one to distinguish this IR from a reference IR. C# is additionally complicated by its automatic boxing and unboxing rules: an object of value type may under some circumstances be `boxed', appearing as an object of reference type, and obeying the reference-type semantics. Technically true, in that pointers in C require some special syntax, but the common idiom is to hide this away by defining a new type: typedef Foo* FooPtr; Now, for any code using the FooPtr type, the data model is the same as Python (or as Java, RB, .NET, etc., again for code that's using only reference types). This is a syntactic transformation rather than a change to the data model, though. -- [mdw] -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Steven D'Aprano st...@remove-this-cybersource.com.au wrote: Er, perhaps you missed that I was replying to Joe Strout. Yes, evidently. My apologies for the mix up! -- [mdw], who obviously should put the keyboard down now. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Mark Wooding wrote: ru...@yahoo.com ru...@yahoo.com wrote: If one accepts that there are a lot of people who post in here that clearly are surprised by Python's assignment semantics, But one should not accept that. One might accept that there are many who post who claim that they are surprised by Python's assignment semantics. Such people are wrong, however, since what they are surprised by is Python's data model, and one reason that they are surprised by Python's data model is because it's not actually explained very well. Agreed. I think the docs, especially those that develop the conceptual model of how Python work at runtime, could use some major attention. and further appear to expect assignment to have copy-like semantics, This expectation is justified and, indeed, satisfied. Python does, most definitely, copy on assignment. What it copies is references, however. This might be clearer if the data model were explained better. ... then where is that expectation coming from? How would anyone develop that expectation if (from a different post in this thread), [Python's] idea of assignment is the same as anyone else's. Because they've fundamentally misunderstood the data model. The very fact that their confusion is ascribed to the wrong thing is strongly indicative of this. If you maintain that reference-like assignment is very common and something every programmer is accustomed to, then where are they getting the copy-like assignment expectations from? But it's not just assignment that deals with references. It's argument passing and storage of compound data as well. (See PLR 3.1.) They expect that assignment copies stuff, because that's what assignment does. Everywhere that I can think of -- except C++, which leaves assignment semantics in hands of the programmer. What they're confused about is what, precisely, it is that gets copied. And that, really, is a result of an inadequate understanding of the data model. ... So it still seems to me that this is a likely explanation to why there is frequent misunderstanding of Python's assignments, and why responding to such misunderstandings with, Python's assignments are the same as other languages', is at best not helpful. That's why I'm not just saying that assignment is the same. I'm also saying that the data model is most definitely not the same as C. I would be willing to bet that most of the confused posters do not distinguish between assignment operation (AO) and data model (DM). Their conceptual box is labeled assignment behavior and includes both AO and DM. They expect that AO+DM in Python will produce the same results in Python as they are used to in the other languages they've used. That the discrepancy comes from the DM part rather than the AO part is pretty irrelevant to them given that world view. So responding to the cry, Python assignment is bizarre! with an indignant, No, it is the same as other common languages, is talking with different vocabularies, unless it's also accompanied with all the other information presented in this thread about how Python treats all names as references (which *is* different that some other languages). I notice there is not even an FAQ on the subject of assignment despite the frequency which which people ask about it. I have often wished that C handled arrays the same way it does structs. I am sure that the pointer-array pseudo-equivalence seemed like a very clever idea at the time but I wonder if Dennis Richie ever had second thoughts about it. Probably. But I think the idea was actually inherited from BCPL, via B. In BCPL, memory is divided into words. Depending on which operator you use, you can treat a particular word as an integer, a floating-point number, or a pointer. An array in BCPL is represented as a pointer to its first element -- always -- and you index it by an expression of the form p!i (a notation inherited by BBC BASIC and Haskell of all things). The array-pointer decay is a compromise position between the BCPL notion of array-as-pointer and the desire to allocate such things with automatic storage duration and have sizeof and so on work properly. Interesting tidbit, thanks. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Rhodri James wrote: On Sat, 10 Jan 2009 00:21:27 -, ru...@yahoo.com wrote: IIRC, Someone posted here that his experience was that 12-year old kids (presumably without programming experience) had no problem with Python and references when described as names given to an object. (From memory, can't locate the post right now.) 'Twas I. It was a rebuttal to your point that Python's assignment, parameter passing and data model is somehow inherently more difficult to wrap your brain around than that of other languages. It isn't; if anything it seems to be easier. That was and is not my point. My point was that there seems to be an observably significant number of people who are surprised by the way Python assignment works. I hypothesized that this was due to their experience with other languages, *not* that Python is somehow inherently more difficult to wrap your brain around, and that the response, Python assignments are the same as in those other languages is insufficient. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Wed, 07 Jan 2009 03:45:00 -0800, sturlamolden wrote: On Jan 7, 2:02 am, Steven D'Aprano ste...@remove.this.cybersource.com.au wrote: In Python code, there are no references and no dereferencing. The why does CPython keep track of reference counts? Two different levels of explanation. At the level of Python code, you don't see reference counts. You never manipulate reference counts directly. The gc module does expose them, if you want to see them, but the gc module deliberately peaks behind the curtains. It's special. At the implementation level, CPython uses references so it needs reference counts. Jython doesn't keep reference counts at all, because it uses Java's garbage collection (or so I understand). And the hypothetical Distributed Python uses clones of objects and a daemon which keeps them in sync, and hence there are no reference counts because each clone is attached once and once only. You can't, because Python doesn't have references. In a language with references, that's easy. Python does not 'pass-by-reference' as Fortran or Pascal do. That's right. But sadly, if you tell people that Python uses references, many people will say How do I pass a reference to this object to a function?. a = 123456789 b = [a] c = [a] d = [a, a] b[0] is a True c[0] is a True d[0] is a True d[1] is a True Where is the object 'a' stored? Somewhere in memory, floating free, where it is referred to under the name 'a'. In CPython, it will be in the heap, unless it has been paged out to disk. In the lists 'b', 'c' and (twice) 'd'. I don't have a problem with objects being in two places at the same time. It's just a mental model. I understand that, underneath, the memory for the object is in *one place*, somewhere, because distributed storage is a hard problem and no existing Python does it. But I also understand that underneath, *everything* is just mutable bytes. There are no ints or strings or lists or dicts, they're just an abstraction. If you keep looking behind the curtains, looking at the implementation of each level of abstraction, eventually you'll get to bytes, and then electrons. If you go there, then you'll conclude that the object 'a' isn't anywhere. I'm happy with a high-level abstraction where Python objects can be in more than one place at once. Now, how do you implement such an abstraction? The easiest way is to have the object in one (hidden?) place, and have everywhere else use a pointer or reference to it. But that's a lower level of description than you can reach from Python code, because you can't access those pointers. You can only infer that they are there because otherwise you have to accept that objects can be in two places at once. Or because you've read the source code, but that's implementation. -- Steven -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Jan 8, 1:45 am, Steven D'Aprano ste...@remove.this.cybersource.com.au wrote: On Wed, 07 Jan 2009 10:17:55 +, Mark Wooding wrote: snip The `they're just objects' model is very simple, but gets tied up in knots explaining things. The `it's all references' model is only a little more complicated, but explains everything. But it *over* explains, because it implies things that everybody knows about references in other languages that aren't true for Python. Of course it's not literally true that everybody knows that you can use references to implement a swap(x, y) procedure. But people coming from a C or Pascal background tend to assume that everything is like C/Pascal, and there are a lot of them. If C was a rare, unfamiliar language, my opposition to using the term reference would be a lot milder. Maybe in another five years? No, indeed. Python is a language for talking about paperweights. And it's because of the paperweights that the duck-typing works: all the paperweights are the same shape and size, so they're physically interchangeable. Okay, the abstraction has leaked again... are the paperweights references to the objects, or the names we've bound objects to? I'm confused... How do we deal with anonymous objects in your model? -- Steven Mark, hi, Steven, pleasure as always. Neither side is perfect or wild; (Do admit it); How do we decide what is best for newcomers to Python, depending on background? -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
ru...@yahoo.com ru...@yahoo.com wrote: I thought you were objecting to Python's use of the term binding when you wrote: [snip] in response to someone talking about ...all those who use the term \name binding\ instead of variable assignment Oh, that. Well, the terms are `binding' and `assignment'. Python doesn't need another name for assignment, because actually its idea of assignment is the same as anyone else's. The difference is in the way it deals with objects. See argument elsewhere. -- [mdw] -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Aaron Brady wrote: On Jan 8, 1:45 am, Steven D'Aprano ste...@remove.this.cybersource.com.au wrote: On Wed, 07 Jan 2009 10:17:55 +, Mark Wooding wrote: snip The `they're just objects' model is very simple, but gets tied up in knots explaining things. The `it's all references' model is only a little more complicated, but explains everything. But it *over* explains, because it implies things that everybody knows about references in other languages that aren't true for Python. Of course it's not literally true that everybody knows that you can use references to implement a swap(x, y) procedure. But people coming from a C or Pascal background tend to assume that everything is like C/Pascal, and there are a lot of them. If C was a rare, unfamiliar language, my opposition to using the term reference would be a lot milder. Maybe in another five years? No, indeed. Python is a language for talking about paperweights. And it's because of the paperweights that the duck-typing works: all the paperweights are the same shape and size, so they're physically interchangeable. Okay, the abstraction has leaked again... are the paperweights references to the objects, or the names we've bound objects to? I'm confused... How do we deal with anonymous objects in your model? -- Steven Mark, hi, Steven, pleasure as always. Neither side is perfect or wild; (Do admit it); How do we decide what is best for newcomers to Python, depending on background? The crux of this mistake is assuming that all beginners are alike, and that there is therefore a single best way to explain things to them. Teaching should be a dialog, in which the teacher makes use of knowledge revealed by the student about their existing knowledge and ways of thinking to present ideas in an easily assimilable form. In other words, don't treat all students the same. regards Steve -- Steve Holden+1 571 484 6266 +1 800 494 3119 Holden Web LLC http://www.holdenweb.com/ -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Absolutely. Trivially and at a high level, teaching python to kids who are learning programming as introductory material teaching python to motivated college graduate students teaching python to adult non-professional programmers with a need to learn python (like for instance, frustrated accountants who have HAD IT with VBA...) The difference between the last 2 is important. I am not now nor will I ever be a professional programmer; there's a depth of knowledge to the subject that I will not ever systematically study (mostly due to time constraints). On the other hand, the problems I want and need to solve, I want to get right and will put in the effort to learn what I need to get it right. Contrast the college student. He's getting a broad and hopefully deep grounding in more CS theory than I have. How much does he care? Well, the beer bash Friday at 4 probably has a higher priority in his life right now, not unreasonably. So you can explain things to me fairly technically (I've done lots of VBA programming, hated it, and am motivated in self-study in python), but not too abstractly, because I don't have a deep ground in the theory behind CS and programming. (What I know of grammar parsing comes via some linguistics work I did in college, not Backus-Naur Form, although Noam chomsky was important in both fields) In contrast, the CS student should get a generalized explanation because he needs to grow the skills to work it out on his own, and because the generalized and theoretically grounded explanation is more useful for him in extrapolating to other areas. If he can't do more with it than i can, he needs to change majors. On 1/8/09, Steve Holden st...@holdenweb.com wrote: Aaron Brady wrote: On Jan 8, 1:45 am, Steven D'Aprano ste...@remove.this.cybersource.com.au wrote: On Wed, 07 Jan 2009 10:17:55 +, Mark Wooding wrote: snip The `they're just objects' model is very simple, but gets tied up in knots explaining things. The `it's all references' model is only a little more complicated, but explains everything. But it *over* explains, because it implies things that everybody knows about references in other languages that aren't true for Python. Of course it's not literally true that everybody knows that you can use references to implement a swap(x, y) procedure. But people coming from a C or Pascal background tend to assume that everything is like C/Pascal, and there are a lot of them. If C was a rare, unfamiliar language, my opposition to using the term reference would be a lot milder. Maybe in another five years? No, indeed. Python is a language for talking about paperweights. And it's because of the paperweights that the duck-typing works: all the paperweights are the same shape and size, so they're physically interchangeable. Okay, the abstraction has leaked again... are the paperweights references to the objects, or the names we've bound objects to? I'm confused... How do we deal with anonymous objects in your model? -- Steven Mark, hi, Steven, pleasure as always. Neither side is perfect or wild; (Do admit it); How do we decide what is best for newcomers to Python, depending on background? The crux of this mistake is assuming that all beginners are alike, and that there is therefore a single best way to explain things to them. Teaching should be a dialog, in which the teacher makes use of knowledge revealed by the student about their existing knowledge and ways of thinking to present ideas in an easily assimilable form. In other words, don't treat all students the same. regards Steve -- Steve Holden+1 571 484 6266 +1 800 494 3119 Holden Web LLC http://www.holdenweb.com/ -- http://mail.python.org/mailman/listinfo/python-list -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
[Steven's message hasn't reached my server, so I'll reply to it here. Sorry if this is confusing.] Aaron Brady castiro...@gmail.com wrote: On Jan 8, 1:45 am, Steven D'Aprano ste...@remove.this.cybersource.com.au wrote: On Wed, 07 Jan 2009 10:17:55 +, Mark Wooding wrote: The `they're just objects' model is very simple, but gets tied up in knots explaining things. The `it's all references' model is only a little more complicated, but explains everything. But it *over* explains, because it implies things that everybody knows about references in other languages that aren't true for Python. I addressed this elsewhere. Summary: `pass-by-reference' is a different thing to `all you manipulate are references': Python does pass-by-value, but the things it passes -- by value -- are references. (The `pass-by-*' notions are confusingly named anyway. Pass-by-name doesn't actually involve names at all.) Of course it's not literally true that everybody knows that you can use references to implement a swap(x, y) procedure. But people coming from a C or Pascal background tend to assume that everything is like C/Pascal, and there are a lot of them. If C was a rare, unfamiliar language, my opposition to using the term reference would be a lot milder. Maybe in another five years? I agree with the comment about Pascal, but C is actually pretty similar to Python here. C only does pass-by-value. If you want a function to modify your variable, you have to pass a pointer value which points to it. Python has no pointer values, so you need a different hack. The hack usually involves lists. (Though it's easier in the main to return compound data objects like tuples. I don't suppose that a proposal for true multiple return values would go down well here. No, didn't think so...) Okay, the abstraction has leaked again... are the paperweights references to the objects, or the names we've bound objects to? I'm confused... They're the references to the objects. You don't bind names to objects. You bind names slots in which you store references. This discussion -- I'd call it an argument, but that might give the wrong impression, because I think we're being remarkably civil and constructive by the standards of Usenet arguments! -- hasn't started on the topic of variable bindings or environments yet. How do we deal with anonymous objects in your model? -- Steven Mark, hi, Steven, pleasure as always. Hello. ;-) Neither side is perfect or wild; (Do admit it); It's true. How do we decide what is best for newcomers to Python, depending on background? That I really don't know. I'm not good at teaching total beginners (does it show?) because I'm too enmired in the theory. (It doesn't help that I go off on tangents about how language X does something similar but subtly different all the time, though my rich background comes in very useful all over the place and that's something I think is worth sharing.) It probably doesn't help that I came to Python with a thorough understanding of Scheme (among many others) under my belt, because many Scheme concepts carry over directly, including the data model (it's all references) and the variable model (nested, mutable, lexical environments with closures). What I am pretty sure of is that references are going to have to enter the picture at some point, because other models get too complicated. Oh, while I remember: the `distributed Python' model, with auto-updating copies, only works for sharing. Circular structures still require actual references or a Tardis. -- [mdw] -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Erik Max Francis m...@alcyone.com wrote: Terry Reedy wrote: a='par'+'rot' b='parrot' a is b True One exactly doesn't really say much. It's implementation dependent, and depends on the length of the string: a = 'this is a much longer ' + 'parrot' b = 'this is a much longer parrot' a is b False That Terry's example works is due to constant folding in the bytecode compiler. Consider: In [1]: a = 'parrot' In [2]: a is 'par' + 'rot' Out[2]: True Fair enough. But build the string in a more complicated way: In [3]: b = 'par' In [4]: a is b + 'rot' Out[4]: False What's going on? In the first case, the compiler notices that both operands to `+' are constants, and evaluates the concatenation at compile-time. The resulting constant string is then interned if it's short enough. Putting part of the string in a variable is enough to stymie this optimization -- the same compiler gets used in functions which can't assume that the variable will still have the same value as it does now. The concatenation method on strings doesn't try to intern the result, as that might be a runtime performance loss. In practice, tests like these are pretty much never useful. It's completely implementation dependent when and under what circumstances fundamental immutable objects are reused, and it's not useful anyway; what you care about is whether two objects are equal or not, not whether they're the same object through some optimization behind the scenes. Absolutely. The examples above provide insight into how the specific implementation actually behaves; but that's of strictly academic interest. (Well, I find that sort of thing interesting, anyway.) -- [mdw] -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Mark Wooding wrote: The `they're just objects' model is very simple, but gets tied up in knots explaining things. The `it's all references' model is only a little more complicated, but explains everything. But it *over* explains, because it implies things that everybody knows about references in other languages that aren't true for Python. I addressed this elsewhere. Summary: `pass-by-reference' is a different thing to `all you manipulate are references': Python does pass-by-value, but the things it passes -- by value -- are references. Quite right. It's easy to see this in languages where some types are references and others are simple values; and even easier in such a language that supports both pass-by-value and pass-by-reference (such as REALbasic or VB.NET). Then you can easily see, in one language, all combinations of [value type, reference type] * [pass by ref, pass by val]. In Python, we only have reference types, and we only have pass by value, so out of the four combinations above, there is only one: references passed by value. You'd think this would make it easier, but from the raging debates and repeated obfuscation on this point, it apparently makes Python MORE difficult to understand and explain (at least for some). I agree with the comment about Pascal, but C is actually pretty similar to Python here. C only does pass-by-value. If you want a function to modify your variable, you have to pass a pointer value which points to it. Right -- a C/C++ pointer is (or at least, can be in normal usage) pretty similar to a reference (though of course you can do more low-level and hackish things with them too). In C, such a reference is always passed by value, as in Python or Java, and just like the default mode in RB or .NET. In C++, you can also choose to pass such a parameter by reference, like the ByRef mode in RB and .NET. This parameter passing mode is unavailable in Python or Java. Okay, the abstraction has leaked again... are the paperweights references to the objects, or the names we've bound objects to? I'm confused... They're the references to the objects. You don't bind names to objects. You bind names slots in which you store references. Well put (again). This is technically the correct description, though in casual usage, I think it's fine to occasionally gloss over some of these layers as long as everyone involved understands what is meant. (This is especially true when the references are to immutable objects, which are functionally very similar to simple values.) How do we decide what is best for newcomers to Python, depending on background? That I really don't know. I'm not good at teaching total beginners (does it show?) because I'm too enmired in the theory. ... FWIW, I've spent a fair amount of time teaching beginners, though not so much in Python yet. But plenty of time in other languages where the same questions come up. In my experience, pointing out that a variable of any object type contains a *reference* to that object, rather than the object data itself, and then illustrating with a couple of examples, quickly clears up any confusion. I've never had a newbie require more than a couple of exchanges on this topic before they get it. (And before I joined the Python community, I never even felt the need to actually draw a picture [1] to make it clearer.) What I am pretty sure of is that references are going to have to enter the picture at some point, because other models get too complicated. I agree completely. I can barely understand the other models myself. Best, - Joe [1] http://www.strout.net/info/coding/valref/ -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Mark Wooding wrote: ... Python doesn't need another name for assignment, OK. because actually its idea of assignment is the same as anyone else's. The difference is in the way it deals with objects. See argument elsewhere. the same as anyone else's only if [Python's] idea of assignment does not include producing the same results. a = array (1,2,3) b = a a[1] = 4 print b C, C++, VBA, Fortran, Perl: 1, 2, 3 Python: 1, 4, 3 Telling someone coming to Python from one of those languages that Python's assignment works the same way as those languages is confusing at best. dealing objects is part of assignment semantics ISTM. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Mark Wooding wrote: ... I agree with the comment about Pascal, but C is actually pretty similar to Python here. C only does pass-by-value. As a side comment (because it always bugs me when I read this, even though I read it in very authoritative sources), ISTM that C passes everything by value except arrays; they are passed by reference (by passing a pointer to the array by value.) Admittedly, the close relationship between arrays and pointers makes it easy conflate them. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
ru...@yahoo.com wrote: the same as anyone else's only if [Python's] idea of assignment does not include producing the same results. a = array (1,2,3) b = a a[1] = 4 print b C, C++, VBA, Fortran, Perl: 1, 2, 3 Python: 1, 4, 3 You are mistaken (except perhaps in the Fortran case, which is an oddball by modern standards, and I don't know Perl well enough to judge). C/C++ code: int* a = malloc(3); a[0] = 1; a[1] = 2; a[2] = 3; int* b = a; a[1] = 4; print_array(b) --- Result: 1, 4, 3 REALbasic code: Dim a() As Integer = Array(1,2,3) Dim b() As Integer = a a(1) = 4 PrintArray b -- Result: 1, 4, 3 VB.NET code would be very similar in syntax, and identical in behavior, to the REALbasic code. Java would also have the same semantics, though my Java is too rusty to get the syntax right without actually trying it. If you can find a language where an array variable is NOT a reference (and thus does not behave exactly as in Python, Java, REALbasic, C++, etc.), then that language is either a dinosaur or some weird academic oddity. Best, - Joe -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Joe Strout wrote: ru...@yahoo.com wrote: the same as anyone else's only if [Python's] idea of assignment does not include producing the same results. a = array (1,2,3) b = a a[1] = 4 print b C, C++, VBA, Fortran, Perl: 1, 2, 3 Python: 1, 4, 3 You are mistaken I don't think so. See http://groups.google.com/group/comp.lang.python/msg/f99d5a0d8f869b96 The code I quoted there was tested. In the C/C++ case, array-to-pointer coercion confuses the issue so I embedded the array in a struct which is more like an object. The assignment semantics are copy-like producing the results I quoted. (Keep in mind my point was not to show the behavior of arrays, but to show that several common languages *do not* use Python's *all* names are references model -- though of course this does not preclude their having some Python-like assignments since they all have some way of doing references.) It seems plausible to me that experience with copy-like assignments semantics in other languages accounts for the frequent misunderstanding of Python assignments that is seen on this list. (except perhaps in the Fortran case, which is an oddball by modern standards, and I don't know Perl well enough to judge). Whether or not Perl is oddball or modern is irrelevant; it is still widely used and it is reasonable to assume that there are a significant number of people coming to Python with a lot of previous experience with Perl. Even Fortran is still used in scientific computing circles (or so I'm told) although I can't say I have seen any c.l.p. postings from people claiming Python doesn't work like fortran. :-) C/C++ code: int* a = malloc(3); a[0] = 1; a[1] = 2; a[2] = 3; int* b = a; a[1] = 4; print_array(b) --- Result: 1, 4, 3 REALbasic code: Dim a() As Integer = Array(1,2,3) Dim b() As Integer = a a(1) = 4 PrintArray b -- Result: 1, 4, 3 VB.NET code would be very similar in syntax, and identical in behavior, to the REALbasic code. Don't know about RealBasic or VB.Net, my experience and quoted results were from MS Visual Basic for Apps which is (I think) based on VB6. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Mark Wooding m.@distorted.org.uk wrote: A better analogy. The objects are scattered across the floor. No object is contained in another. However, we have a plentiful supply of bits of string, each of which is tied to a Teflon-covered paperweight at one end and has a blob of Blu-Tack on the other. Instead of putting something in a box directly, what we do is grab a piece of string, stick the Blu-Tack to the thing, and put the paperweight in the box. This way, we can stick several bits of string to the same object and put the paperweights in different boxes. Indeed, nothing stops us sticking two bits of string to a box, and putting both paperweights inside that same box. But fitting a box into itself twice requires origami skills that frighten me. The Teflon stops the Blu-Tack from sticking to the paperweights, 'cos you're not allowed to do that. There's a daemon who comes around periodically and cleans up the mess of paperweights which aren't in boxes, and tidies away things which don't have any string stuck to them any more, but really he's just an implementation detail and you wouldn't need him if your floor was big enough and you had enough bits of sufficiently long string. Lovely! This is the nicest analogy I have seen, and it seems to completely account for all the observed effects. The only other one that comes close is Dennis L Bieber's Wandering Names. I propose that we name the garbage collection demon Steven because he has IMO lost this argument, and therefore deserves to spend eternity tied up in string. ;-) - Hendrik -- With the disappearance of the gas mantle and the advent of the short circuit, man's tranquillity began to be threatened by everything he put his hand on. (James Thurber. First sentence of Sex ex Machina) -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Wed, 07 Jan 2009 03:49:02 +, Mark Wooding wrote: Steven D'Aprano ste...@remove.this.cybersource.com.au wrote: The only tricky thing is that items 1, 2 and 3 can be inside two different boxes at the same time. There's no obvious real world analogy to that without the boxes being nested. This ability for objects to be in two places at once (or even to be inside themselves!) is one of the few reasons why Python's use of references in the implementation needs to be mentioned. Ahh. So it /does/ need to be mentioned after all. Only in the sense that the behaviour of *real world* objects don't entirely match the behaviour of Python objects. If you just accept that Python objects can be in two places at once, an unintuitive concept I accept but hardly difficult to grasp, then you don't need to mention references. (Of course, as a purely practical matter, the English language makes it difficult to avoid the word reference entirely. That's not my intention.) But you're wrong: it's not an implementation detail: it's an essential part of the language semantics. Er, pardon me, but you yourself suggested that one could implement Python without using references to objects, like keeping lists of clones, and magically updating all the clones whenever one us mutated. That may even be a valid implementation for a distributed Python. Like you suggested, it would be a seriously rotten model for standard Python, but it is possible. The language semantics specifies the *behaviour*, not the *mechanism* required to implement that behaviour. A better analogy. The objects are scattered across the floor. No object is contained in another. However, we have a plentiful supply of bits of string, each of which is tied to a Teflon-covered paperweight at one end and has a blob of Blu-Tack on the other. Instead of putting something in a box directly, what we do is grab a piece of string, stick the Blu-Tack to the thing, and put the paperweight in the box. Not a bad analogy. I like it. But it still fails. Why can't I stick the paperweight in the box *before* attaching the Blu- Tack to something else, and then forget about attaching the Blu-Tack? There's nothing in your model to prevent dangling pointers, except hand- waving it doesn't work like that. I assume the string is made of Teflon, otherwise I could stick the Blu- Tack to another piece of string. By the same token, the floor needs to be Teflon too. Why can't I grab the string by the end with the Blu-Tack and follow it backwards to find out where the paperweight is? I obviously know how to handle the end, because according to your model I'm sticking it to other objects. I suppose maybe there's a robot that does that Blu-Tack sticking for me, I just point to the object and say That one! and it happens. The string itself is invisible except to radar, which the robot has, and I don't. That way I can't follow the string backwards to find out where the paperweight is. Hmmm. This model is getting awfully complicated: Teflon string, Teflon paperweights, Blu-Tack, *and* a robot (presumably Teflon as well), none of which are visible to Python code, as well as objects which are. This way, we can stick several bits of string to the same object and put the paperweights in different boxes. Indeed, nothing stops us sticking two bits of string to a box, and putting both paperweights inside that same box. But fitting a box into itself twice requires origami skills that frighten me. Ah, you've obviously never studied origami under Sensei Ping of the Clan of the Pointed Stick! The Teflon stops the Blu-Tack from sticking to the paperweights, 'cos you're not allowed to do that. There's a daemon who comes around periodically and cleans up the mess of paperweights which aren't in boxes, and tidies away things which don't have any string stuck to them any more, So there's a brief moment between creating the object and sticking the Blu-Tack on it when the daemon might take the object and the string away? No, I guess not, that's another thing I just have to take on faith... the daemon knows not to touch anything until the robot has finished with it. Can I tie two pieces of string together, stop the daemon from disposing of them? Apparently not. Objects in two places at one time isn't sounding that weird any more. I actually do like your model, despite poking holes in it. As a model for what CPython is doing under the hood, it pretty good. But it's not a model for what a Python programmer does when writing Python code. There's no action equivalent to attach blob of Blu-Tack to object, no action equivalent to put the paperweight in the box. The robot does that when you point to an object and say put that inside the box. The programmer doesn't do these things, he says put that object in the box and the robot attaches the Blu-Tack and moves the paperweight. The strings and paperweights and Blu-Tack are
Re: why cannot assign to function call
Steven D'Aprano ste...@remove.this.cybersource.com.au wrote: Only in the sense that the behaviour of *real world* objects don't entirely match the behaviour of Python objects. If you just accept that Python objects can be in two places at once, an unintuitive concept I accept but hardly difficult to grasp, then you don't need to mention references. To my mind, explaining that objects can actually be in two (or three, or any unbounded number) of places at once, stashed inside each other to an arbitrary degree -- that looks like the excuses of someone who's been caught in a lie. Maybe it was a useful lie-to-children, meant to avoid explaining the full details in advance, but the time comes to put away childish things, and learn the truth. (I can't believe I just quoted Paul. I feel all dirty.) Er, pardon me, but you yourself suggested that one could implement Python without using references to objects, like keeping lists of clones, and magically updating all the clones whenever one us mutated. That may even be a valid implementation for a distributed Python. Like you suggested, it would be a seriously rotten model for standard Python, but it is possible. The language semantics specifies the *behaviour*, not the *mechanism* required to implement that behaviour. Yes, there's an isomorphism between the two, so technically either could be used as a model to explain the behaviour of Python. But, to my mind at least, the idea of references (or sticky bits of string) is the simpler -- it doesn't involve doing anything`magically' -- so Occam suggests that it's superior. I'm sure, if I tried, I could come with an even more absurd model, or express it in more complicated ways, but the power of a model lies in its combination of simplicity and explanatory power. The `they're just objects' model is very simple, but gets tied up in knots explaining things. The `it's all references' model is only a little more complicated, but explains everything. It might be -- and I don't know, because I'm not an educator -- that the `they're just objects' model hits a local maximum in the tradeoff between simplicity and explanation that's sufficiently high that it's useful for teaching beginners. It's hard to expose this simple model's weaknesses without assignment (or messing with `is' or `id'); unfortunately, it's very easy to explose them once you do have assignment (or any kind of mutable state). And Python's purely functional subset is rarely used exclusively in nontrivial programs. So I'm sceptical that this particular lie-to-children doesn't cause more harm than good. Why can't I stick the paperweight in the box *before* attaching the Blu- Tack to something else, and then forget about attaching the Blu-Tack? There's nothing in your model to prevent dangling pointers, except hand- waving it doesn't work like that. Perils of posting at half past three in the morning, I'm afraid. I seemed to have hit a creative streak, but my clarity of thinking was definitely impaired. You are only allowed to handle paperweights. A robot does the rest. Some of the objects might give you splinters or paper cuts. The robot is concerned about your safety and won't let you go near them. There's a wall between you and the playpen, with a catflap in it. The robot will fit through the catflap but you're too big. (For advanced users only: there might be a secret hatch which lets you into the main arena where the robot works, via a storeroom containing a surprising quantity of handy power tools, heavy weaponry, and explosives, but oddly lacking in safety goggles. You can build some truly marvellous things by sneaking through here, and persuade the robot to do things it wouldn't usually by threatening it at gunpoint, but you can also blow yourself up.) You tell the robot what you want him to do by handing him paperweights. He doesn't take the paperweights away with him, but instead hooks onto the string so that he can follow it to the other end. He's quite good at following even tangled strings, and at finding paperweights in boxes and hooking onto their strings too. The robot will stick new strings onto objects at your request and hand you the paperweights. I assume the string is made of Teflon, otherwise I could stick the Blu- Tack to another piece of string. By the same token, the floor needs to be Teflon too. You could do, if you were actually given a loose piece of string. But you aren't, so that's OK. I was wrong that the paperweights needed to be Teflon. Why can't I grab the string by the end with the Blu-Tack and follow it backwards to find out where the paperweight is? Because you're never at that end. And the robot just won't do that. (The daemon in the MIT Scheme environment /can/ do this for you. It uses a feature called `wabbit hunting'. You set up a `Fudd thunk' describing which objects you want it to find the paperweights of, and set the thing to `wabbit season'. The daemon
Re: why cannot assign to function call
On Jan 7, 2:02 am, Steven D'Aprano ste...@remove.this.cybersource.com.au wrote: In Python code, there are no references and no dereferencing. The why does CPython keep track of reference counts? You can't, because Python doesn't have references. In a language with references, that's easy. Python does not 'pass-by-reference' as Fortran or Pascal do. It's harder (impossible?) to write a version that will operate on arbitrary types, but that's statically typed languages for you. In Python an assignment (re)binds the name to another value. You can certainly write a swap method for mutable types. But you cannot use the assignment operator to swap the values. No no no, lists and tuples store *objects*. a = 123456789 b = [a] c = [a] d = [a, a] b[0] is a True c[0] is a True d[0] is a True d[1] is a True Where is the object 'a' stored? -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Wed, 07 Jan 2009 03:45:00 -0800, sturlamolden wrote: On Jan 7, 2:02 am, Steven D'Aprano ste...@remove.this.cybersource.com.au wrote: In Python code, there are no references and no dereferencing. The why does CPython keep track of reference counts? Show me the *Python* code that does that. Using reference counting for memory management is an implementation detail. It's possible to use other garbage collectors without the need of reference counting. Ciao, Marc 'BlackJack' Rintsch -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Wait a sec... I think I get this... In essence, the implication of immutability for Python is that there is only one parrot, one spam,in fact one anything. (This seems like it must hold for data primitives - does it hold for complex objects as well? It seems it must...) In addition there is only one 1, and one 2 etc. We may or may not have realized that string in a memory address to which variable names can be bound, but should we do so, there is only one parrot Python, is in fact, a Platonic programming language. Weird. If I've got this right, worth chewing on On 1/2/09, Derek Martin c...@pizzashack.org wrote: On Tue, Dec 30, 2008 at 02:21:29PM +, John O'Hagan wrote: Fortunately, unlike the murky world of philosophy, Python (AIUI) simplifies this question by simply declaring that yes, in the case of mutable objects, we may say that we are still referring to the same object although we've changed it, and no, in the case of immutable objects, we may not, and must exchange it if we want a different value (a word too fraught with ambiguity in this context to use unquoted!). That's sort of true; it would seem to be more accurate to say that whenever a name is assigned to an object and subsequently reassigned, the name no longer is associated with the original object. In the case of mutable objects, the object can be changed by performing an assignment of *part* of the object through its original name, i.e. strings may be mutable, but the following code still produces two different objects: a = 'hello' a = 'goodbye' The first object so created is orphaned; it's been given the Russian non-person treatment. It still exists, but the authorities (i.e. the python interpreter) don't acknowledge it and provide the rest of the world no way to communicate with it, and eventually it is reaped by the garbage collector. :) What the Python community often overlooks, when this discussion again rears its ugly head (as it seems to every other hour or so), is that its assignment model is BIZARRE, as in it's conceptually different from virtually all other languages substantially taught in undergraduate computer science programs. And for that matter, it's pretty unintuitive generally. That is, in what I'll call normal computer languages, a variable name is thought of as the address of a bin where some data is stored, and the name is inexorably tied to that bin. You can change what's in the bin, but the name you gave the bin always points to the same bin. This tends to be conceptually true even if it might technically not be true in a given implementation of a language. Python is very different from this. Names are not addresses of bins; they are instead simply ephemeral labels which are given to bins, where the bin is a Python object which contains specific data at the time of assignment. A second assignment of that name doesn't change what's in the original bin; it actually (probably) first creates a new bin, then removes the name from the original bin and assigns it to the new one. Intuitively, it's a bit like saying your kitchen table is no longer a kitchen table, and now the thing where you wash your dishes is a kitchen table. It doesn't really make a lot of sense (whether or not it's so for good reason), and it makes describing the assignment model necessarily convoluted, whereas the named bins model from the majority of other languages people are likely to have been exposed to is simple and sensible. It's small wonder that neophytes try to cram Python behaviors into terms and computing concepts they already understand from learning other languages, and that they fail to do so. What's mystifying is that when Pythonistas reply to their messages, they universally seem confused at how this could possibly happen, and often enough actually seem offended (or at least offensive) when it inevitably does happen... -- Derek D. Martin http://www.pizzashack.org/ GPG Key ID: 0x81CFE75D -- http://mail.python.org/mailman/listinfo/python-list -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Dan Esch wrote: Wait a sec... I think I get this... In essence, the implication of immutability for Python is that there is only one parrot, one spam,in fact one anything. (This seems like it must hold for data primitives - does it hold for complex objects as well? It seems it must...) In addition there is only one 1, and one 2 etc. We may or may not have realized that string in a memory address to which variable names can be bound, but should we do so, there is only one parrot Python, is in fact, a Platonic programming language. Weird. If I've got this right, worth chewing on 'Fraid not. Certain immutables are cached by the interpreter, but most are not. s1 = a + b + c n = 12345 s2 = ab + chr(99) m = 2469 * 5 s1 == s2 True s1 is s2 False n == m True n is m False id(s1), id(s2), id(n), id(m) (2146661888, 2146661792, 14453620, 14453584) regards Steve -- Steve Holden+1 571 484 6266 +1 800 494 3119 Holden Web LLC http://www.holdenweb.com/ -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Okay, thanks... Still trying to wrap my fragile little VBA-corrupted brain around names, namespaces, and objects. Progress is being made. For me, the naive idea of variable == label for bin has been hard to get past simply because someone in the the back of my head is screaming, Wait, if the VARIABLE doesn't point to a memory address, (somewhere down the implementation stack) whatinthehell does?? Further reading clarifies: For object X, id(X) is an immutable attribute reference that can ultimately be bound to a memory address (he said, blithely skipping over several layers of architecture...) So, okay, now I can relax. To oversimplify (told you VBA warped my mind) everything in python is an object (at some level). At minimum, all objects have an identity-- id(X), a type which subclasses and extends it from object, and some content, the nature of which depends on the object type. Once I got my head around the idea that there was something that was a fixed point of reference for the object, nevermind what, then I could relax and get on with get on with getting my head around names and namespaces. Thanks for listening to me ramble. On 1/7/09, Steve Holden st...@holdenweb.com wrote: Dan Esch wrote: Wait a sec... I think I get this... In essence, the implication of immutability for Python is that there is only one parrot, one spam,in fact one anything. (This seems like it must hold for data primitives - does it hold for complex objects as well? It seems it must...) In addition there is only one 1, and one 2 etc. We may or may not have realized that string in a memory address to which variable names can be bound, but should we do so, there is only one parrot Python, is in fact, a Platonic programming language. Weird. If I've got this right, worth chewing on 'Fraid not. Certain immutables are cached by the interpreter, but most are not. s1 = a + b + c n = 12345 s2 = ab + chr(99) m = 2469 * 5 s1 == s2 True s1 is s2 False n == m True n is m False id(s1), id(s2), id(n), id(m) (2146661888, 2146661792, 14453620, 14453584) regards Steve -- Steve Holden+1 571 484 6266 +1 800 494 3119 Holden Web LLC http://www.holdenweb.com/ -- http://mail.python.org/mailman/listinfo/python-list -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Jan 6, 9:20 pm, Mark Wooding m...@distorted.org.uk wrote: ru...@yahoo.com ru...@yahoo.com wrote: Is not the proper term aliasing? Perhaps Python variables should be called alises. No. The proper term is most definitely `binding': see SICP, for example. (Wikipedia has a link to the full text.) I thought you were objecting to Python's use of the term binding when you wrote: Unfortunately, this practice causes other confusion, since `binding' is often used (in other language communities, notably Lisp and the functional languages) to describe the association between names and slots that hold references. in response to someone talking about ...all those who use the term \name binding\ instead of variable assignment -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Dan Esch wrote: In essence, the implication of immutability for Python is that there is only one parrot, one spam,in fact one anything. (This seems like it must hold for data primitives - does it hold for complex objects as well? It seems it must...) In addition there is only one 1, and one 2 etc. That's not necessarily true. If you have a = par + rot b = parrot then, most likely (though it depends on how clever the compiler optimizations are), there are two different string objects containing the data parrot. In this case, a == b is true because string equality testing compares the data, but a is b is false because a and b refer to different objects. And by the way, don't let the people claiming that Python's assignment model is bizarre confuse you. Assignment and parameter-passing in Python is exactly the same as in any other modern OOP language; it just so happens that in Python, all variables are references, while in most other languages, some variables are references and others are primitive types. But it should be obvious (though apparently isn't, to some) that this restricted, uniform data model does not fundamentally change anything; it just makes Python a little more restricted and uniform. For details, see: http://www.strout.net/info/coding/valref/ Best, - Joe -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Joe Strout wrote: That's not necessarily true. If you have a = par + rot b = parrot then, most likely (though it depends on how clever the compiler optimizations are), there are two different string objects containing the data parrot. a='par'+'rot' b='parrot' a is b True -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Jan 5, 12:21 pm, Derek Martin c...@pizzashack.org wrote: ... I understand why the assignment model works the way it does, and it's quite sensible, *when you understand it*. However, I do also think that to someone who has not encountered such a model before, and who has not had it explained to them, and/or who has not the background to understand why it is implemented that way, it very likely might seem markedly unusual in appearance, style, or general character and often involving incongruous or unexpected elements; as dictionary.com defines the term bizarre. So no, I don't think that's a mischaracterization at all. ... Ignoring the nit-picking over choice of words, or what languages are taught in CS courses, I agree with Derek's general point that Python's assignment semantics is *not* the same as many other common languages, and that this confuses people coming to Python from those languages, as evidenced by frequent postings to this list by those people. I concluded exactly the same a long time ago. When I started using Python I had no problem with Python's assignment semantics because I had been using references in Perl for years. I did not have a very hard time with Perl's references, after I recognized their similarities to C's pointers. But as I recall, it took a *long* time to wrap my mind around C pointers. Here is the output of set of equivalent programs in five common (at least at one time) languages (code below) that I have worked in and that formed my intuition about how assignment works. Sure looks to me as though Python is the odd man out. Perl: 1, 4, 3 1, 2, 3 Basic (MS VBA): 1 4 3 1 2 3 C: 1, 4, 3 1, 2, 3 Fortran: 1 4 3 1 2 3 Python: [1, 4, 3] [1, 4, 3] Of course, one can dismiss these as non-mainstream or non-modern languages, and maintain that all important languages have the same assignment semantics as Python, but then one is left having to explain the frequent confusion about assignment that is visible on this list. I find Derek's explantion more plausible than any others I have read in this thread. Python: #/usr/bin/python a = [1,2,3] b = a a[1] = 4 print a print b Perl: #!/usr/bin/perl @a = (1,2,3); @b = @a; $a[1] = 4; print join (',', @a), \n; print join (',', @b), \n; Basic: Sub test() Dim a, b a = Array(1, 2, 3) b = a a(1) = 4 Debug.Print a(0), a(1), a(2) Debug.Print b(0), b(1), b(2) End Sub C: #include stdio.h main () { int i; struct {int a[3];} a, b; for (i=0; i3; i++) { a.a[i] = i+1; } b = a; a.a[1] = 4; printf (%i,%i,%i\n, a.a[0], a.a[1], a.a[2]); printf (%i,%i,%i\n, b.a[0], b.a[1], b.a[2]); } == Fortran: program test integer a(3), b(3) do 100, i=1,3 a(i) = i 100 continue b = a a(2) = 4 write(unit=*,fmt=*) a write(unit=*,fmt=*) b end -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Terry Reedy wrote: Joe Strout wrote: That's not necessarily true. If you have a = par + rot b = parrot then, most likely (though it depends on how clever the compiler optimizations are), there are two different string objects containing the data parrot. a='par'+'rot' b='parrot' a is b True One exactly doesn't really say much. It's implementation dependent, and depends on the length of the string: a = 'this is a much longer ' + 'parrot' b = 'this is a much longer parrot' a is b False In practice, tests like these are pretty much never useful. It's completely implementation dependent when and under what circumstances fundamental immutable objects are reused, and it's not useful anyway; what you care about is whether two objects are equal or not, not whether they're the same object through some optimization behind the scenes. -- Erik Max Francis m...@alcyone.com http://www.alcyone.com/max/ San Jose, CA, USA 37 18 N 121 57 W AIM, Y!M erikmaxfrancis All delays are dangerous in war. -- John Dryden, 1631-1700 -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Wed, Jan 7, 2009 at 7:22 PM, Terry Reedy tjre...@udel.edu wrote: Joe Strout wrote: That's not necessarily true. If you have a = par + rot b = parrot then, most likely (though it depends on how clever the compiler optimizations are), there are two different string objects containing the data parrot. a='par'+'rot' b='parrot' a is b True (this is on Mono. I don't know if the real .NET and/or newer versions of IronPython do this differently) IronPython 1.1 (1.1) on .NET 2.0.50727.42 Copyright (c) Microsoft Corporation. All rights reserved. a = par + rot b=parrot a is b False -- http://mail.python.org/mailman/listinfo/python-list -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Thu, 08 Jan 2009 00:45:06 -, ru...@yahoo.com wrote: When I started using Python I had no problem with Python's assignment semantics because I had been using references in Perl for years. I did not have a very hard time with Perl's references, after I recognized their similarities to C's pointers. But as I recall, it took a *long* time to wrap my mind around C pointers. I'd suggest that part of the reason for that is C's blurring of the line between arrays and pointers. You can treat them interchangeably enough of the time that when you can't it catches you by surprise. My experience of teaching twelve-year olds Python is that they understand assignment when it's explained in terms of giving names to these objects (numbers, strings, wombats...) that we've already shown them. It's a lot harder to get them to understand the putting an object into a labelled box model when working with other languages. I don't know why this should be, but it is. -- Rhodri James *-* Wildebeeste Herder to the Masses -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Rhodri James wrote: On Thu, 08 Jan 2009 00:45:06 -, ru...@yahoo.com wrote: When I started using Python I had no problem with Python's assignment semantics because I had been using references in Perl for years. I did not have a very hard time with Perl's references, after I recognized their similarities to C's pointers. But as I recall, it took a *long* time to wrap my mind around C pointers. I'd suggest that part of the reason for that is C's blurring of the line between arrays and pointers. You can treat them interchangeably enough of the time that when you can't it catches you by surprise. My experience of teaching twelve-year olds Python is that they understand assignment when it's explained in terms of giving names to these objects (numbers, strings, wombats...) that we've already shown them. It's a lot harder to get them to understand the putting an object into a labelled box model when working with other languages. I don't know why this should be, but it is. Kids have little experience with being 'the guest in Room 203' or 'the inmate in cell a-23'. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Wed, 07 Jan 2009 10:17:55 +, Mark Wooding wrote: Steven D'Aprano ste...@remove.this.cybersource.com.au wrote: Only in the sense that the behaviour of *real world* objects don't entirely match the behaviour of Python objects. If you just accept that Python objects can be in two places at once, an unintuitive concept I accept but hardly difficult to grasp, then you don't need to mention references. To my mind, explaining that objects can actually be in two (or three, or any unbounded number) of places at once, stashed inside each other to an arbitrary degree -- that looks like the excuses of someone who's been caught in a lie. Maybe it was a useful lie-to-children, meant to avoid explaining the full details in advance, but the time comes to put away childish things, and learn the truth. (I can't believe I just quoted Paul. I feel all dirty.) All analogies are lies-to-children. I don't think this is any worse than the lie-to-children about magic pieces of string that you can only handle by the paperweight at one end. Both analogies contain leaky abstractions. The `they're just objects' model is very simple, but gets tied up in knots explaining things. The `it's all references' model is only a little more complicated, but explains everything. But it *over* explains, because it implies things that everybody knows about references in other languages that aren't true for Python. Of course it's not literally true that everybody knows that you can use references to implement a swap(x, y) procedure. But people coming from a C or Pascal background tend to assume that everything is like C/Pascal, and there are a lot of them. If C was a rare, unfamiliar language, my opposition to using the term reference would be a lot milder. Maybe in another five years? No, indeed. Python is a language for talking about paperweights. And it's because of the paperweights that the duck-typing works: all the paperweights are the same shape and size, so they're physically interchangeable. Okay, the abstraction has leaked again... are the paperweights references to the objects, or the names we've bound objects to? I'm confused... How do we deal with anonymous objects in your model? -- Steven -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Steven D'Aprano ste...@remove.this.cybersource.com.au wrote: By all means, if Derek doesn't like the assignment model used by Python (and Java, Ruby, C#, Perl, RealBasic, VisualBasic, Lua, and many other languages going back to at least CLU in the mid 1970s) It goes back to Lisp in the late 1950s. [snip stuff I agree with.] -- [mdw] -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Steven D'Aprano st...@remove-this-cybersource.com.au wrote: (3) Those who come from an entirely different programming model, say, Forth or Haskell. For them, Python's assignment model is going to be the least of their worries. Actually, Haskell's assignment model (you have to grubbing about for IORefs or STRefs to find it but it's there) is exactly the same as Lisp, Scheme, Python, Dylan, Perl, Lua, Icon, Ruby, Erlang, ML, ... The variables-and-containers-hold-references-to-objects idea is common to a very large number (probably the majority? depends how you count) of high-level languages. Java is in the same category. C# is definitely not, since `struct's are composite, mutable value types clearly distinguishable from anything Python can do. The main dissent is probably from Tcl, which pretends to copy values -- even complex objects such as lists -- wholesale (though actually does fancy copy-on-write things under the covers). Your first post in this topic made the extraordinarily arrogant and incorrect claim that: [quote] What the Python community often overlooks, when this discussion again rears its ugly head (as it seems to every other hour or so), is that its assignment model is BIZARRE, as in it's conceptually different from virtually all other languages substantially taught in undergraduate computer science programs. [end quote] We can see some pretty poor assumptions right there: * That the Python community is capable of uniformly overlooking Python's differences from other languages. * That only languages substantially taught in undergraduate CS courses matter. And a massive assumption about the languages taught to CS undergrads too. In particular, Scheme is a fairly popular language to teach to undergrads, often as a first language. See, for example, `The Structure and Interpretation of Computer Programs' by Abelson and Sussman, and the (much more recent) `How to Design Programs' by Felleisen, Findler, Flatt and Krishnamurthi. It seems that you don't include in the Python community all those who use the term name binding instead of variable assignment specifically because it gives new users a clue that Python is not the same as C. Unfortunately, this practice causes other confusion, since `binding' is often used (in other language communities, notably Lisp and the functional languages) to describe the association between names and slots that hold references. Let me explain. I'll try to be clear, though I know that Stephen already understands this stuff well. At run-time, each named variable denotes a storage area (I'll call it a slot[2]) which holds a reference to some object[1]. +-+ name | ref - object +-+ If we `assign' a different object to the variable, what really happens is that the slot is modified to refer to the other object; but the name continues to denote the same slot. Usually `binding', or `rebinding', a name describes a different process, whereby the name (for a while) denotes a different slot. This name-to- slot mapping is important because it's the mapping which is inherited by nested functions. This is most clearly shown by example. Consider this Python interaction. In [1]: l = [] In [2]: for i in [1, 2, 3]: ...: l.append(lambda: i) ...: In [3]: [f() for f in l] Out[3]: [3, 3, 3] This prints [3, 3, 3]. What's happened is that the lambdas have closed over the prevailing /binding/ of i -- i.e., which reference slot it denotes. Since the for loop operates by assignment, and /not/ by rebinding i, the three lambdas all closed over the same environment, and return the same value. Scheme's DO loop, by contrast, really does operate by binding. Consider this example of a GNU Guile interaction. guile (define l '()) guile (do ((i (list 1 2 3) (cdr i))) ...((null? i)) ... (set! l (append l (list (lambda () (car i)) guile (map (lambda (f) (f)) l) $1 = (1 2 3) That is, each time through the loop, the variable I denotes a /different/ reference slot; the LAMBDAs close over different environments, and the functions return different values. (This example illustrates both binding and assignment, since the SET! changes the value referred to in the slot denoted by L, without rebinding L.) A very approximate translation of the above into Python looks like this. In [1]: l = [] In [2]: items = [1, 2, 3] In [3]: def _loop(ix): ...: if ix = len(items): return ...: l.append(lambda: items[ix]) ...: _loop(ix + 1) ...: In [4]: _loop(0) In [5]: [f() for f in l] Out[5]: [1, 2, 3] (I've cheated and used indices because Python lists don't have the head/tail structure of Lisp lists.) None of this is a criticism of Python's behaviour. (Sometimes it'd be nice if `for' worked by rebinding, but sometimes it's better that it doesn't.) But it is a criticism of the Python community's confusing use of the word `binding' to mean something different from
Re: why cannot assign to function call
Steven D'Aprano ste...@remove.this.cybersource.com.au wrote: I don't think so. Variables in algebra are quite different from variables in programming languages. Contrast the statement: x = x+1 as a programming expression and an algebraic equation. As a programming expression, it means increment x by one. But as an algebraic expression, it means x is some value such that it is equal to one more than itself, and there is no solution to such an equation. Surely there is. The solution is: 1 = 0 (and hence x is an -- no, /the/ -- element of the trivial ring). and how several generations of computer languages, not to mention the actual machine language those generated, behaved, before the current crop. Sure. And? Actally, this is true only for /very/ small values of `several'. There was FORTRAN in 1957, with what you're calling the `named bins' model, and I call assignment-is-copying, and Lisp in 1958, with the assignment- is-pointer-diddling model. -- [mdw] -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Derek Martin c...@pizzashack.org wrote: I think I have though, not that it matters, since that was never really my point. Python's assignment model requires more explanation than the traditional one, simply to state what it does. That alone is evidence (but not proof). Hmm. Actually, it's not the assignment model which is strange at all. It's the data model. What does an expression like [1, 2, 3] denote? Is it the list itself, or a /reference/ to the list? If you answer the first, you'll want Tcl/C/Fortran semantics. If you answer the second, you'll want Lisp/Python/Javascript semantics. If you answer `it depends', you'll want to be confused. Python decided that all values are passed around as and manipulated through references. (There are no locatives: references are not values, and you can't have a reference to a reference.) Lists store references; tuples store references; and so on. If one is to be able to implement complex data structures which involve sharing of data (e.g., cyclic graphs) or which allow efficient reorganization (e.g., various kinds of trees) we need to be able to manage references to values somehow. There are a number of ways of doing this. * Everything is a reference. This is the most uniform. Once you've got the hang of it, there are no surprises. * Nothing is a reference. If you want complex data structures, you're either screwed (e.g., if your data model is too weak) or you have to invent them yourself using array indices or something wretched like that. Tcl is in this category: you have to mess with (associative) arrays or variable names to make complex data structures. * Some values are references to others. This is like C, and causes exciting problems when you get confused. C++ makes things more confusing because dereferencing can happen implicitly. Perl manages to be in this camp /and/ the first one. It's very strange. * Some things are references, and some aren't. This is what Java and C# do. Java says that only simple, atomic things are non-references, so assignment looks the same either way, but you can't store primitive things in containers expecting to store references (until Sun added automatic boxing and unboxing -- eventually). C# makes matters more complicated by letting users define structured mutable data which aren't handled by reference, but does the boxing and unboxing thing transparently. As for there being no assignment in algebra, is that not really what variable substitution is? No. They have different names, but in my estimation they perform exactly the same function. You're assigning specific values to the variables in an expression so that you can solve for a constant... just like in a computer program. When you do this, you construct a different expression. They might have equal values, but they're still different. (You can consider equivalence classes under some appropriate equivalence relation if you like, but that just makes things more complex.) There is even an allocation of memory: it's in your brain. :D Ahh! You're modifying a memory location in your brain so that it refers to a different expression. Isn't this the Lisp/Python/Javascript model? ;-) -- [mdw] -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
Mark Wooding wrote: Derek Martin c...@pizzashack.org wrote: I think I have though, not that it matters, since that was never really my point. Python's assignment model requires more explanation than the traditional one, simply to state what it does. That alone is evidence (but not proof). Hmm. Actually, it's not the assignment model which is strange at all. It's the data model. What does an expression like [1, 2, 3] denote? Is it the list itself, or a /reference/ to the list? If you answer the first, you'll want Tcl/C/Fortran semantics. If you answer the second, you'll want Lisp/Python/Javascript semantics. If you answer `it depends', you'll want to be confused. Well said! You can easily see that assignment in Python is perfectly ordinary, by comparing it to languages that have both values and references (such as C++, Java, or REALbasic). Those languages have only one assignment model, that operates on both values and references just fine. Python has only references, and I think it's for this reason that some people here try to pretend that it doesn't have them at all, thus leading them to weird explanations of strange assignment and argument-passing behavior. Python decided that all values are passed around as and manipulated through references. (There are no locatives: references are not values, and you can't have a reference to a reference.) Also very clearly put. If you don't mind, I may crib some of your verbage for http://www.strout.net/info/coding/valref/, as it may be clearer than my own. Best, - Joe -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Jan 2, 5:43 pm, Steve Holden st...@holdenweb.com wrote: Derek Martin wrote: On Tue, Dec 30, 2008 at 02:21:29PM +, John O'Hagan wrote: [...] What the Python community often overlooks, when this discussion again rears its ugly head (as it seems to every other hour or so), is that its assignment model is BIZARRE, as in it's conceptually different from virtually all other languages substantially taught in undergraduate computer science programs. And for that matter, it's pretty unintuitive generally. I'd definitely argue against bizarre. It's actually very easy to understand, and Python is by no means the only language to have used it. A statement like f(x) = 1 has no meaning in most languages - including Python, C and Java. C++ references allows operator= to be overloaded. It is one of few languages where a statement like 'f(x) = 1;' maybe meaningful. But C++ references are addresses (pointers in disguise), not names. The = is Python's name binding operator. Python is similar to Java in this respect. If anything is bizarre here, it is C++ references. P.S. The C statement '*f(x) = 1;' does have meaning, but 'f(x) = 1;' do not. But in the former case, one is not assigning to the return value. Python and Java do not have raw pointers like C. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Jan 2, 11:39 am, Derek Martin c...@pizzashack.org wrote: What the Python community often overlooks, when this discussion again rears its ugly head (as it seems to every other hour or so), is that its assignment model is BIZARRE, as in it's conceptually different from virtually all other languages substantially taught in undergraduate computer science programs. And for that matter, it's pretty unintuitive generally. For one thing, Python's 'assignment model' (an annoying buzzword) is similar to that of Java. It cannot be that different from what is thought in undergraduate CS classes. But take a look at C++: int foobar(int x) { // blahblah } Now a statement like 'foobar(x) = y;' can actually have a meaning. Whereas, int* foobar(int x) { // blahblah } would make the same statement illegal. I have yet to see undergraduate students knowing the difference between C++ pointers and references after attending an introductory CS course. Try to do the same in, say, Java. You will see that is behaves like Python. And as for bizarreness, I feel that Python and Java is greatly surpassed by C++. Note that C do not share the bizarreness of C++. -- http://mail.python.org/mailman/listinfo/python-list
Re: why cannot assign to function call
On Jan 6, 8:03 am, Mark Wooding m...@distorted.org.uk wrote: Steven D'Aprano st...@remove-this-cybersource.com.au wrote: snip It seems that you don't include in the Python community all those who use the term name binding instead of variable assignment specifically because it gives new users a clue that Python is not the same as C. Unfortunately, this practice causes other confusion, since `binding' is often used (in other language communities, notably Lisp and the functional languages) to describe the association between names and slots that hold references. Let me explain. I'll try to be clear, though I know that Stephen already understands this stuff well. At run-time, each named variable denotes a storage area (I'll call it a slot[2]) which holds a reference to some object[1]. +-+ name | ref - object +-+ If we `assign' a different object to the variable, what really happens is that the slot is modified to refer to the other object; but the name continues to denote the same slot. Usually `binding', or `rebinding', a name describes a different process, whereby the name (for a while) denotes a different slot. This name-to- slot mapping is important because it's the mapping which is inherited by nested functions. Say you start with: +--+ a | ref1 - (1, 2, 3) +--+ Then you can do: +--+ a | ref2 - (4, 5, 6) +--+ But not: +--+ a | ref1 - (4, 5, 6) +--+ That is, you can 'repoint a' to another 'ref', but not repoint a 'ref'. I think one of the ideas we have trouble communicating is that [1, 2, 3] and [4, 5, 6] can be the same object (using '[:]='), but [1, 2, 3] and [1, 2, 3] don't have to be. -- http://mail.python.org/mailman/listinfo/python-list