what's in a name (was Re: using lambda to print everything in a list)

Alex Martelli aleaxit at yahoo.com
Mon Apr 30 05:05:16 EDT 2001


"Steven D. Majewski" <sdm7g at Virginia.EDU> wrote in message
news:mailman.988387842.394.python-list at python.org...
    [snip]
> > My personal impression is that lambda is a (minor)
> > nuisance.  Naming the code fragment that you want
> > to pass to some other function, by making it a
> > nested function, seems clearer & handier to me.
>
> OK Alex, I'll volunteer an explaination:
>
> Lambda is typically used for temporary, throw-away functions in
> expressions.

It appears to me that the typical use is a subset of this,
specifically to pass some function as an argument -- often,
in particular, to map (or, I guess, apply or reduce, but
those uses seem rarer).

>  You could say exactly the same thing about anonymous (unnamed,
> temporary) values in expressions. Requiring every value to be
> named wouldn't at all change the capabilities of a language.

You COULD say "the same thing", but that would not be "the
language capabilities don't change"; what I was specifically
saying is that naming "a code fragment" (to be passed as an
argument to some other function), rather than leaving it
unnamed, seems to me to result in clearer code.  So, "the
same thing", if said of subexpression-values, would be that
the naming seemed to result in clearer code.

That would be pretty controversial, although I *would* accept
a watered-down equivalent: IF an expression is complicated,
THEN sensibly breaking it up into more than one expression
_and giving "intermediate values" *significant* names_ may
well result in a net gain of clarity and readability.

Consider:

assert (n/math.e)**n*math.sqrt(2*n*math.pi) < af < \
    (n/math.e)**n*(1+1/(12.*n-1))*math.sqrt(2*n*math.pi)

A "pretty obvious" sanity check of a Stirling approximation...
maybe.  I find this expression rather too rich for my blood,
and I believe it WOULD be clearer and handier to break it up
into sensibly-named sub-expressions, despite the advantage
it has, in this form, to "magically disappear" from optimized
code -- the broken-up equivalent needs explicit __debug__
testing:

if __debug__:
    stirling_exponential = (n/math.e) ** n
    stirling_square_root = math.sqrt(2*n*math.pi)
    lower_bound = stirling_exponential * stirling_square_root
    fudge_factor = 1.0 + 1.0/( (12.0*n) - 1.0 )
    upper_bound = lower_bound * fudge_factor
    assert lower_bound < af < upper_bound

OK, this _may_ be overdoing it, the names are rather iffy,
the explicit float literals and use of whitespace in the
computation of fudge_factor are debatable -- probably the
sweet spot in writing this out is somewhere in-between the
single super-duper formula and this too-detailed break-up.


But the "sweet spot" when what is to be named or un-named
_regarding a code fragment_ (rather than the result of
some intermediate expression) seems to me to be far skewed
in favour of naming.  Often you see lambdas used for things
that HAVE a perfectly-understandable name already:
    reduce(lambda x, y: x+y, seq)
rather than
    reduce(operator.add, seq)
being a typical example, and we HAVE seen posts suggesting
    map(lambda x: write(x), seq)
rather than
    map(write, seq)
and often, on the other hand, you see lambdas stretched
to the bursting point to perform processing that is quite
a bit more complex than what they're meant for -- that's
often where FAQ 4.16 gets into play, for example.

There MAY be a tiny "window of opportunity" where the
lambda is a sweet-spot for something that IS quite simple
but still not "natively provided" by Python, as in "call
method 'spam' on each object in the list" -- here, the
alternative (if and when offered)
    map(ClassName.spam, seq)
IS generally inferior to the lambda
    map(lambda x: x.spam(), seq)
because the latter is polymorphic, while the former is
not (a BIG price to pay...!); still, the NAMED alternative:
    def spamIt(x): return x.spam()
    map(spamIt, seq)
seems roughly on a par with the lambda one (I personally
prefer it, I can well see somebody else preferring the
lambda in this case, but surely the difference is tiny
one way or another... so "lambda" isn't really "pulling
its own weight" when considered as a language feature...).

If somewhere in the builtins was the equivalent of

class Method:
    def __init__(self, name):
        self.name = name
    def __call__(self, obj, *args, **kwds):
        bound_method = getattr(obj, self.name)
        return bound_method(*args, **kwds)

so we could portably write something like:
    map(Method('spam'), seq)

lambda's "window of perhaps-reasonable opportunity"
would narrow still further.  (This would need more
design-work, to let some arguments to the call be
curried at init-time, etc -- consider it the merest
of strawmen^H^H^Hpeople...).


> Sometimes it's more clear to leave some things unnamed.

Occasionally, it may be -- more often, it's _handier_
by enough that lower-clarity risks are deemed worth
paying (and sometimes that's the wrong tradeoff).

> ( Most human languages have pronouns, don't they ?
>   Since everyone so enjoyed the language side-thread:
>    are there any pronoun-free languages ? )

Not as far as I recall from my time as a computational
linguist -- even creoles and pidgins, it appears, tend
to develop some kind of pronominal forms pretty soon
("simpler" ones than other languages, most often, but...).
And similarly, there is no human language that is free
from pronominal ambiguity.

Of course, the tradeoffs in a programming language (or
other formal language) are very different from those
in languages intended for human use.  In the latter
case, for example, a measure of ambiguity, and ability
to obfuscate without actually lying, _is_ part of the
actual "design goals", whether acknowledged or not,
because often the alleged "communication" will in fact
be meant as an attempt by one party to gain some kind
of advantage over another.  This is rarely the case in
computer programming (rarely, not never -- "obfuscated
C" contexts show a counterexample).


> It's exactly the same case with anonymous functional expressions.
> It's a conveniece.

I sometimes muse whether "convenience" has overtaken "optimization"
as the alleged reason for the worst design errors in software.  Not
that I consider lambda a candidate for "worst design error", mind
you, the musing is a general one.

Simplicity and regularity appear to deliver more _real_ long-term
convenience than most 'convenience'-inspired irregularities and
complications.

_In the context of Python_ (as opposed to, say, Haskell), I don't
find myself "composing functions" with wild abandon.  I do pass
functions to other functions, etc, but in recent times I find
myself using named local functions instead of unnamed lambdas for
that, more and more -- lately, I would say, exclusively.  And I
don't miss the lambdas at all...


Alex






More information about the Python-list mailing list