On 2014-02-21, at 18:30 , Chris Angelico <rosuav@gmail.com> wrote:
So here's the proposal. Introduce a new operator to Python, just like the dot operator but behaving differently when it returns a bound method. We can possibly use ->, or maybe create a new operator that currently makes no sense, like .. or .> or something. Its semantics would be:
As Yuri noted the concept exists, AFAIK it was introduced by smalltalk as "message cascading". Basically, the ability to send a sequence of messages to the same subject without having to repeatedly specify the subject. I believe Dart is the first language to have resurrected this feature so far. The cascading operator in smalltalk was `;` (the message-send operator is the space), so e.g. foo message ; message2: aParameter ; message3. would send all of message, message2:aParameter and message 3 to `foo`, in that specific order. In smalltalk, a cascade returns the result of the last message in the cascade. smalltalk provides a `yourself` operator to return the subject itself. Cascading is very commonly used to initialise collections as smalltalk was born at a time where literal high-level collections were not exactly a thing: aCollection := (OrderedCollection new) add: 1 ; add: 2 ; add: 3 ; youself.
1) Look up the attribute following it on the object, exactly as per the current . operator 2) If the result is not a function, return it, exactly as per current. 3) If it is a function, though, return a wrapper which, when called, calls the inner function and then returns self.
I could be wrong, but I'm pretty sure this is an over-complication when you look at it at the bytecode level: you can load the subject as many times as you've got attr accesses to do on it, or you could have an alternate attr access which puts TOS back. No need for a wrapper.
Effectively, x->y would be equivalent to chain(x.y):
def chain(func): def chainable(self, *args, **kwargs): func(self, *args, **kwargs) return self return chainable
Could be useful in a variety of contexts.
Thoughts?
No need for a wrapper. Where `a.b` compiles to LOAD_FAST a LOAD_ATTR b POP_TOP `a->b` would compile to LOAD_FAST a DUP_TOP LOAD_ATTR b POP_TOP at this point you've got an a left on the stack and can reuse it: `a->b()->c()->d()` would be LOAD_FAST a DUP_TOP LOAD_ATTR b CALL_FUNCTION POP_TOP DUP_TOP LOAD_ATTR c CALL_FUNCTION POP_TOP DUP_TOP LOAD_ATTR d CALL_FUNCTION POP_TOP The tail end of the cascade would be slightly more complex in that it would be: ROT_TWO POP_TOP so that the subject is discarded and the value of the last attribute/method is available on the stack (it may get popped as well if it's unused). Or maybe it would do nothing special and the cascade would yield (or pop) the subject unless closed by an attribute access or regular method call. That would avoid the requirement of a `yourself`-type method when initialising mutables, although the final irregularity may lack visibility.