Method chaining

Steven D'Aprano steve+comp.lang.python at pearwood.info
Fri Nov 22 11:26:06 EST 2013


On Fri, 22 Nov 2013 16:20:03 +0100, Peter Otten wrote:

> Steven D'Aprano wrote:
> 
>> On Fri, 22 Nov 2013 13:08:03 +0100, Peter Otten wrote:
>> 
>>> These things are nice to write as long as you omit the gory details,
>>> but personally I don't want to see the style it favours in my or other
>>> people's code.
>> 
>> There's not really a lot of difference
> 
> That cuts both ways ;)

Actually, I was wrong. See below.

>> between:
>> 
>>     obj = MyClass()
>>     obj.spam()
>>     obj.eggs()
>>     obj.cheese()
>> 
>> and
>> 
>>     obj = MyClass().spam().eggs().cheese()
>> 
>> 
>> except the first takes up a lot more vertical space.
> 
> I've not yet run short of vertical space ;)

However, here is a real difference:

# With chaining
thing = func(MyClass().spam().eggs().cheese(), 
             MyClass().aardvark(),
             OtherClass().fe().fi().fo().fum(),
             )
do_stuff_with(thing)


versus:

# Without chaining
temp1 = MyClass()
temp1.spam()
temp1.eggs()
temp1.cheese()
temp2 = MyClass()
temp2.aardvark()
temp3 = OtherClass()
temp3.fe()
temp3.fi()
temp3.fo()
temp3.fum()
thing = func(temp1, temp2, temp3)
do_stuff_with(thing)


In this case the chained version doesn't obscure the intention of the 
code anywhere near as much as the unchained version and its plethora of 
temporary variables.


>> Chained method calls is idiomatic in some languages.
> 
> Languages with mutable objects?

Yes. It's a "Design Pattern" applicable to any language with mutator 
methods. Here are three examples in C#, Java and Ruby:

http://mrbool.com/fluent-interface-and-method-chaining-a-good-programming-approach/26365
http://www.infoq.com/articles/internal-dsls-java
http://blog.jayfields.com/2008/03/ruby-replace-temp-with-chain.html

although in fairness I wouldn't call it idiomatic in C# or Java.

Ruby 1.9 even added a new method to Object, tap, specifically to allow 
chaining of methods, which itself was copied from Ruby-On-Rails' 
"returning" helper. So I think it's fair to say that method chaining
for mutation is idiomatic in Ruby.

http://www.seejohncode.com/2012/01/02/ruby-tap-that/

http://blog.moertel.com/posts/2007-02-07-ruby-1-9-gets-handy-new-method-object-tap.html


This idea goes back to Smalltalk, and is essentially just a 
pipeline. Hardly something weird.

http://en.wikipedia.org/wiki/Method_chaining

(Technically, if the methods return self rather than None, it's method 
cascading. Chaining is any sequence of method calls, whether they return
self or something else.)

Dart includes syntax for method cascades, which I'm told looks like 
this:

x = SomeClass()
    ..spam()
    ..eggs()
    ..cheese()


The technique also comes with the blessing of Martin Fowler, where it
is an important part of fluent interfaces:

http://martinfowler.com/bliki/FluentInterface.html

Quote:

"The common convention in the curly brace world is that modifier 
methods are void, which I like because it follows the principle 
of CommandQuerySeparation. This convention does get in the way of 
a fluent interface, so I'm inclined to suspend the convention for 
this case."


And last but not least, if you want to go all the way down to the lambda 
calculus and combinator theory, my "selfie" adapter function is just a 
form of the K-combinator (a.k.a. the Kestrel). So there's a deep and 
powerful mathematical pedigree to the idea, if that matters.



-- 
Steven



More information about the Python-list mailing list