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