[Python-ideas] Method chaining notation

Chris Angelico rosuav at gmail.com
Sat Feb 22 05:40:59 CET 2014


On Sat, Feb 22, 2014 at 9:31 AM, Masklinn <masklinn at masklinn.net> wrote:
> On 2014-02-21, at 18:30 , Chris Angelico <rosuav at 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.

Cascading is what I'm looking for here, yes. As noted in the Wiki page
Yuri linked to, chaining-with-return-self enables cascading. Consider
this to be a proposal to add method cascading to Python. Also, since
Dart uses .., that's a good reason to use .. here too.

>> 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.

That would be a job for the peephole optimizer. What happens if you do this:

func = x..y
# more code
func().z

It can't just leave x on the stack, but it has to have the same semantics.

But I agree, the DUP_TOP form would be excellent for the common case:

> 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
>
> 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.

Yes, it would do that. If you use .. everywhere, then the end result
of the whole expression should be the original object. In Pike GTK,
where most methods return themselves, I can do this:

object window = GTK2.Window(0)
    ->set_title("Title!")
    ->add(some_object)
    ->show_all();

The return value from show_all() is the original window. With explicit
method cascading, I could either capture the return value of the last
function call by choosing _not_ to use cascading there, or I could
capture the original object by continuing the cascade. (In the case of
GUI work like this, I'd default to cascading, if I were not using the
result of the expression. It'd mean that adding or deleting lines of
code wouldn't risk changing anything - it's like permitting a trailing
comma in a tuple/list.)

ChrisA


More information about the Python-ideas mailing list