Steven showed a more abstract solution than the one I tried but Cameron is making some good points on whether it might not be a great idea to chain some side-effect operations.
I have seen languages where everything seems to be immutable. Python does this in places like with tuples. The idea is that every change results in a new copy of things, sort of. But in such a world, there is no real concept of making a change internally. The name pointing to the object is all that remains the same. The underlying object is a new copy if any changes are needed. So if I made a deep copy of "this" and returned that, then what would happen to the original? Would it still be changed for anything else that cared? When I am doing a pipeline, I really may not care about the original. At every point I care about propagating my changes. " Hello World ".lower() becomes " hello world " at that point. If I then add a .rstrip() and a .lstrip() each produces a new string without some whitespace. I don't care if the original is affected. If I then add a request to get just part of the string, again. The original is intact. The only time it is changed is when I assign the result back to the original variable and even then, anything else also pointing to it is unchanged. Python has plenty of operators of multiple kinds. Some return a shallow copy and some a deep copy and some an altered copy and some an altered original and some change NOTHING whatsoever. Some make subtle changes in the parent class for example which may later impact the child but are not stored in the child. And efficiency is also a concern. Returning something when not needed is not efficient and can result in having to do something to suppress. Brief digression to make the point. R defaults to returning the last evaluated item in a function with no explicit return statement. Python returns None. So sometimes a line typed at the console generates a print of the returned value. In some cases, the automatic print generates a graph, as in a ggplot object. So some functions take care to mark the returned value as invisible. It is there if you ask for it by saving the result to a variable but does not otherwise print by default. So I can easily see why the design of some features is to not do more than you have to. If the goal is to change the current object, you can simply show the darn object afterwards, right? Well, no, not easy when using a pipeline method. Still, how much would it hurt to allow a keyword option on those methods people WANT to call in a pipeline when it makes sense. Why not let me say object.sort(key=int, reverse=True,displayCopy=True) or something like that. If that messes up threads, fine. Don't use them there. The side effect issue is not to be taken lightly. I believe that may be similar to why there is no ++ operator. But they are adding := which arguably is also a side effect. -----Original Message----- From: Tutor <tutor-bounces+avigross=verizon....@python.org> On Behalf Of Cameron Simpson Sent: Tuesday, December 25, 2018 8:44 PM To: tutor@python.org Subject: Re: [Tutor] decomposing a problem On 26Dec2018 01:06, Alan Gauld <alan.ga...@yahoo.co.uk> wrote: >On 26/12/2018 00:00, Avi Gross wrote: >> great. Many things in python can be made to fit and some need work. >> Dumb example is that sorting something internally returns None and >> not the object itself. > >This is one of my few complaints about Python. >In Smalltalk the default return value from any method is self. In >Python it is None. >self allows chaining of methods, None does not. [...] >Smalltalk uses this technique so much it has its own code layout idiom >(Pythonised as >follows): > >object > .method1() > .method2() > .method3() > .... > .lastone() While I see your point, the Python distinction is that methods returning values tend to return _independent_ values; the original object is not normally semanticly changed. As you know. To take the builtin sorted() example, let us soppose object is a collection, such as a list. I would not want: object.sort() to return the list because that method has a side effect on object. By contract, I'd be happy with a: object.sorted() method returning a new list because it hasn't changes object, and it returns a nice chaining capable object for continued use. But that way lies a suite of doubled methods for most classes: one to apply some operation to an object, modifying it, and its partner to produce a new object (normally of the same type) being a copy of the first object with the operation applied. To me it is the side effect on the original object which weighs against modification methods returning self. Here's a shiny counter example for chaining. thread1: print(object.sorted()) thread2: print(object.sorted(reverse=True)) The above employs composable methods. And they conflict. When methods return a copy the above operation is, loosely speaking, safe: thread1: print(sorted(object)) thread2: print(sorted(object,reverse=True)) Cheers, Cameron Simpson <c...@cskk.id.au> _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor