Is this a good use for lambda

Nick Coghlan ncoghlan at iinet.net.au
Tue Dec 21 04:58:56 EST 2004


Alan G Isaac wrote:
> I need a clarification of the argument.
> Are the opponents saying that I should not be able to:
> 
> def compose(list_of_functions): return reduce(lambda f, g: lambda x:
> f(g(x)), list_of_functions)

As things stand, I can live with lambda. However, if something can't be said in 
a single expression, then it's too complicated to be embedded inside another 
statement and have the whole remain even remotely readable.

Even the above example is impossible to parse easily unless you happen to 
recognise that the 'compose' means 'function composition'. (cf. Fredrik's 
reaction. . .)

> In a nutshell: why?

I dislike the use of lambda here, because it isn't very readable for most 
people, and readability counts. To actually understand what this code does, I 
had to rewrite it as I have below.

To parse a statement using lambdas and figure out what it does, one almost *has* 
to assign a mental name to each lambda. Why should every reader of the code have 
to go to that effort, when the author of the code can do it once, and provide 
premade mental handles for every code reader to come after them?

> And may I see the proposed "better" replacement for function composition.

Moving to a programming world where whitespace and typed characters are so cheap 
as to be practically free:

def compose(list_of_functions):
   def compose_pair(f, g):
     def composed_pair(x):
       f(g(x))
     return composed_pair
   return reduce(compose_pair, list_of_functions)

The advantage of this version is that what it does is fairly obvious, even to 
someone that doesn't often use the term 'function composition' (like, say, me - 
I know what it means, but it doesn't spring to mind naturally).

Moving on, I'd suggest that for any significant list of functions, the 
performance of the recursive version is going to be lousy, and I'd rewrite it 
with the iterative version:

def compose(list_of_functions):
   application_order = reversed(list_of_functions)
   def composed(x):
     for f in application_order:
       x = f(x)
     return x
   return composed

With this last version, even people that don't use functional programming at all 
can see immediately what the function is doing, including what the order of 
application is for the functions that combine to give the overall function 
composition (I had to go look up the precise operation of reduce() to be sure I 
had the order of application for function composition correct). The initial 
creation is going to be much faster (creating a single function object, rather 
than a big stack of them), and the actual invocation is going to be faster 
(since it's a simple iteration, rather than a big chain of recursive function 
calls).

What are the disavantages of my final version?

1. It will deeply offend the aesthetic sensibilities of any functional purists 
in the audience.

2. It no longer directly implements the theoretical concept of function 
composition (although it has the same effect).

 From a real world programming point of view, though, it's self-documenting, 
runs faster and uses less memory, so it's really a pure win.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at email.com   |   Brisbane, Australia
---------------------------------------------------------------
             http://boredomandlaziness.skystorm.net



More information about the Python-list mailing list