I sing the praises of lambda, my friend and savior!

Dave Benjamin ramen at lackingtalent.com
Tue Oct 12 00:29:11 CEST 2004


In article <10mlsct69hmtgca at corp.supernews.com>, Jeff Shannon wrote:
> gabriele renzi wrote:
>>
>> then what you want is a better lambda. One where you can use return, 
>> maybe, and  where you can have statements and multiple expressions.
>> Don't throw away the baby with the bathwater.
> 
> But because of Python's line- and indentation-based syntax, doing all of 
> that on a single line is... um... awkward at best, to put it mildly.  

This is true. I think that Ruby's code block syntax offers a workable
alternative. Many will dispute this, saying it's too ugly, but this is
subjective and not based on any technical reasons. Here is what I've
proposed in the past:

def with_open_file(filename, proc):
    f = open(filename)
    try:
        proc(f)
    finally:
        f.close()
        
with_open_file('data.txt', {|f|
    for line in f:
        print line
})

This is nearly identical to Ruby's syntax, except that instead of creating
an object with a call method, it creates a callable.

> And once you make a lambda multiline, then you lose most of the point of 
> it -- at least, as far as I understand what the point is (having an 
> in-line, anonymous callable).  Once you allow statements and multiple 
> expressions, all you're gaining is anonymity, which seems like a pretty 
> paltry benefit to me.

It's not so much the anonymity that matters, it's the ability to use them as
expressions. It allows you to create your own control structures without
disrupting the logical flow of your program. For instance, right now in
Python you'd have to write the above like this:

def doit(f):
    for line in f:
        print line

with_open_file('data.txt', doit)

To me, this reads, "When I say 'doit', I mean iterate through each line in
some given object 'f', and print that line. Now, with the open file, 'doit'."
Whereas, in the previous example, I'm saying, "With the open file, for each
line in the file, print the line." The difference is subtle, perhaps, but
the need to define a named function beforehand rearranges my code in a way
that I'm not particularly fond of.

Another example is the "after" procedure. If you're familiar with Tcl, you
may recognize the following idiom:

after(10, lambda: sprite.move(0, 5))

In an (imaginary) event-driven framework, this would wait 10 seconds before
moving "sprite" down 5 pixels. You might say that this could easily be
rewritten with a def, and you're right:

def doit():
    sprite.move(0, 5)
after(10, doit)

Now, imagine you are setting up an animation this way:

after(10, lambda: sprite.move(0, 5))
after(15, lambda: sprite2.rotate(30))
after(20, lambda: sprite.scale(120, 120))
after(22, lambda: sprite2.move(50, 50))
after(22, lambda: sound1.play())
after(23, lambda: sprite.move(0, 0))
after(26, lambda: sound1.stop())

Imagine what happens when each one of these lambdas turns into a two-line
def. You could group the "def"s together, perhaps, but then you'd have to
give them all names like "move_sprite_0_5", which is totally redundant.
There are other ways to skin this cat, like switching to a more data-driven
model, but this is a practical use of anonymous functions; I do this kind of
thing all the time in JavaScript and ActionScript, which allow inline
anonymous functions.

> I agree that, if a case is *very* useful, it is possible to break the 
> rules.  List comprehensions, for example, break some of the rules, but 
> they are indeed very useful, so the rule-breakage is more forgiveable.

As was pointed out already in this thread, decorators break many of the same
rules. So far, I find anonymous functions much more useful than decorators.
The only problem is finding a suitable syntax, and I will admit this is
indeed a problem.

> Maybe I'm just ignorant or behind the times, but I still haven't seen a 
> real argument as to *why* lambdas are so useful.  There's some 
> assertions about how avoiding giving something a name makes things 
> clearer, which I disagree with.  ('Explicit is better than implicit.'  I 
> don't see how 'no name' makes the purpose of a code segment clearer than 
> 'some meaningfully descriptive name', no matter how many times it's 
> asserted.)

In that case, why stop at functions? Why not make all intermediate values
have mandatory names? Why not ban this:

c = math.sqrt(a * a + b * b)

And require instead:

a2 = a * a
b2 = b * b
tmp = a2 + b2
c = math.sqrt(tmp)

> There's complaints that it takes a whole two extra lines to 
> type a proper function def, as opposed to being able to use lambdas 
> inline... but again, I'd claim that with a properly named function, the 
> intent at point-of-use will be quite clear and the cost of two extra 
> lines of code is minimal.  (I also tend to break complex expressions 
> into multiple steps to increase clarity -- packing as much as possible 
> into a single line doesn't strike me as all that desirable.)  There's 
> complaints about polluting the namespace... but ISTM that it's not 
> *that* hard to come up with uniquely descriptive names.  Lambdas may 
> once have had some utility in capturing and tinkering with the scoping 
> of names, but that became moot with the introduction of nested scopes 
> 'way back when.
> 
> I understand that lambdas are very popular in other programming 
> languages (such as Lisp).  But Python is not those languages, and which 
> constructs are useful may well be different.  I don't know enough Lisp 
> to judge how helpful lambdas are there; I do know enough Python to 
> believe that I should be able to see the advantages if they were as 
> wonderful as their proponents say.

Lisp is not the only language that benefits from lambdas. Other languages
include JavaScript/ActionScript, C# (anonymous delegates), Java (anonymous
inner classes -- a poor substitute), Ruby, Tcl, Perl, and OCaml. They are
used in GUI callbacks, iteration patterns, flow control, data processing,
and in general any function or procedure that needs parts of its logic to be
parameterized. Only one of the aforementioned languages, OCaml, is
considered to be a functional language like Lisp or Scheme.

> My suspicion is that all of the 
> Python users who like lambdas originally discovered them in other 
> languages and are comfortable with them from that environment, but those 
> who have no previous exposure to lambdas tend to be unimpressed (at 
> best) by Python's take on them.  This renders them, IMO, in the same 
> category as the ternary operator that's continually proposed by converts 
> from, say, C/C++ -- a feature whose usability is much greater in other 
> languages than in Python, and whose inclusion would only satisfy those 
> familiar with the feature from those other languages.  The only 
> difference between the two features, IMO, is that someone managed to 
> talk Guido into including lambdas (which he reportedly regrets), and 
> nobody managed to talk him into including the ternary operator.

I actually discovered lambdas in Python first (well, technically Tcl, but I
didn't know it at the time), and since then I have done a lot of programming
in Python. In fact, it would be safe to say that Python biases my use of
other languages to a greater degree than any other language biases my use of
Python. I don't use lambdas very often, but their use does come up, and I
would rather see them become more powerful (or see code blocks added to the
language) than have them be removed entirely. I'd like to see a ternary
operator too. Guido would have probably added one by now, but nobody could
agree on which syntax would be most "Pythonic". The same fate, I fear, is in
store for any sufficiently advanced anonymous function syntax in Python.

(Yet, somehow, decorators slipped through, even though nobody agreeed on a
syntax for that either. I don't have a rational explanation for that...)

-- 
 .:[ dave benjamin: ramen/[sp00] -:- spoomusic.com -:- ramenfest.com ]:.
        "talking about music is like dancing about architecture."



More information about the Python-list mailing list