[Python-Dev] functools.compose to chain functions together (original) (raw)

Steven D'Aprano steve at pearwood.info
Mon Aug 17 09:43:52 CEST 2009


On Mon, 17 Aug 2009 08:10:16 am Martin v. Löwis wrote:

I don't think he did. Comparing it to the one obvious solution (use a lambda expression), his only reasoning was "it is much easier to read". I truly cannot believe that a compose function would be easier to read to the average Python programmer: if you have

def foo(data): return compose(a, b(data), c) what would you expect that to mean?

foo is a factory function that, given an argument data, generates a function b(data), then composes it with two other functions a and c, and returns the result, also a function.

Please rewrite it as a regular Python expression, preferably without looking at the patch that has been proposed first. I bet there is a 50% chance that you get it wrong (because there are two possible interpretations).

But surely only one of them will agree with the standard definition of function composition. Both Mathworld and Wikipedia agree that f∘g(x) is equivalent to f(g(x)):

http://mathworld.wolfram.com/Composition.html http://en.wikipedia.org/wiki/Function_composition

and I don't see any reason why a compose() function shouldn't do the same.

(Aside: how do I look at the patch? The only link I have is here: http://mail.python.org/pipermail/patches/2007-February/021687.html but I can't see how to get to the patch from there.)

foo could be written as:

def foo(data): return lambda *args, **kwargs: a(b(data)(c(*args, **kwargs)))

Or without lambda:

def foo(data): def composed(*args, **kwargs): return a(b(data)(c(*args, **kwargs))) return composed

This soon gets unwieldy:

def foo(arg1, arg2, arg3): return compose( f, g, h, factory(arg1), factory(arg2), factory(arg3) )

versus

def foo(arg1, arg2, arg3): return lambda *a, **kw: ( f(g(h(factory(arg1)(factory(arg2)(factory(arg3)(*a, **kw)))))) )

but presumably composing six functions is rare.

A further advantage of compose() is that one could, if desired, generate a sensible name and doc string for the returned function. Depends on how heavyweight you want compose() to become.

I think the compose() version is far more readable and understandable, but another factor is the performance cost of the generated function compared to a hand-made lambda.

For the record, Haskell makes compose a built-in operator:

http://www.haskell.org/haskellwiki/Function_composition

It doesn't appear to be standard in Ruby, but it seems to be commonly requested, and a version is on Facets:

http://facets.rubyforge.org/apidoc/api/core/classes/Proc.html#M000161

-- Steven D'Aprano



More information about the Python-Dev mailing list