A critic of Guido's blog on Python's lambda
aleax at mac.com
Thu May 11 03:16:24 CEST 2006
Joe Marshall <eval.apply at gmail.com> wrote:
> Alex Martelli wrote:
> > Joe Marshall <eval.apply at gmail.com> wrote:
> > ...
> > > The problem is that a `name' is a mapping from a symbolic identifier to
> > > an object and that this mapping must either be global (with the
> > > attendant name collision issues) or within a context (with the
> > > attendant question of `in which context').
> > Why is that a problem? Even for so-called "global" names, Python
> > supports a structured, hierarchical namespace, so there can never be any
> > collision betwen the "globals" of distinct modules (including modules
> > which happen to have the same name but live in distinct packages or
> > subpackages) -- I did mention that names could usefully be displayed in
> > some strcutured form such as apackage.somemodule.thefunction but perhaps
> > I was too tangential about it;-).
> Can you refer to inner functions from the global context? Suppose I
> have this Python code:
> def make_adder(x):
> def adder_func(y):
> sum = x + y
> return sum
> return adder_func
> Can I refer to the inner adder_func in any meaningful way?
You can refer to one instance/closure (which make_adder returns), of
course -- you can't refer to the def statement itself (but that's a
statement, ready to create a function/closure each time it executes, not
a function, thus, not an object) except through introspection. Maybe I
don't understand what you mean by this question...
> > If I used continuations (I assume you mean in the call/cc sense rather
> > than some in which I'm not familiar?) I might feel the same way, or not,
> > but I don't (alas), so I can't really argue the point either way for
> > lack of real-world experience.
> I meant continuations as in the receiver function in
> continuation-passing-style. If you have a function that has to act
> differently in response to certain conditions, and you want to
> parameterize the behavior, then one possibility is to pass one or more
> thunks to the function in addition to the normal arguments. The
Ah, OK, I would refer to this as "callbacks", since no
call-with-continuation is involved, just ordinary function calls; your
use case, while pretty alien to Python's typical style, isn't all that
different from other uses of callbacks which _are_ very popular in
Python (cfr the key= argument to the sort methods of list for a typical
example). I would guess that callbacks of all kinds (with absolutely
trivial functions) is the one use case which swayed Guido to keep lambda
(strictly limited to just one expression -- anything more is presumably
worth naming), as well as to add an if/else ternary-operator. I still
disagree deeply, as you guessed I would -- if I had to work with a
framework using callbacks in your style, I'd name my callbacks, and I
wish Python's functools module provided for the elementary cases, such
def ignore_args(*a): return k
def identity(v): return v
and so on -- I find, for example, that to translate your
> (define (option3 key table default-value)
> (lookup key table
> (lambda (value) value)
> (lambda () default-value)))
I prefer to use:
def option3(key, table, default_value):
return lookup(key, table, identity, constant(default_value))
as being more readable than:
def option3(key, table, default_value):
return lookup(key, table, lambda v: v, lambda: default_value)
After all, if I have in >1 place in my code the construct "lambda v: v"
(and if I'm using a framework that requires a lot of function passing
I'm likely to be!), the "Don't Repeat Yourself" (DRY) principle suggests
expressing the construct *ONCE*, naming it, and using the name.
By providing unnamed functions, the language aids and abets violations
of DRY, while having the library provide named elementary functions (in
the already-existing appropriate module) DRY is reinforced and strongly
supported, which, IMHO, is a very good thing.
More information about the Python-list