A critic of Guido's blog on Python's lambda

Kaz Kylheku kkylheku at gmail.com
Mon May 8 17:27:28 EDT 2006


Steve R. Hastings wrote:
> On Fri, 05 May 2006 21:16:50 -0400, Ken Tilton wrote:
> > The upshot of
> > what he wrote is that it would be really hard to make semantically
> > meaningful indentation work with lambda.
>
> Pretty much correct.  The complete thought was that it would be painful
> all out of proportion to the benefit.
>
> See, you don't need multi-line lambda, because you can do this:
>
>
> def make_adder(x):
>     def adder_func(y):
>         sum = x + y
>         return sum
>     return adder_func

Now imagine you had to do this with every object.

  def add_five(x)
    # return x + 5   <--  anonymous integer literal, not allowed!!!
   five = 5   # define it first
   return x + five

Think about the ramifications of every object having to have a name in
some environment, so that at the leaves of all expressions, only names
appear, and literals can only be used in definitions of names.

Also, what happens in the caller who invokes make_adder? Something like
this:

   adder = make_adder(42)

Or perhaps even something like this

  make_adder(2)(3) --> 5

Look, here the function has no name. Why is that allowed? If anonymous
functions are undesireable, shouldn't there be a requirement that the
result of make_adder has to be bound to a name, and then the name must
be used?

> Note that make_adder() doesn't use lambda, and yet it makes a custom
> function with more than one line.  Indented, even.

That function is not exactly custom. What is custom are the environment
bindings that it captures. The code body comes from the program itself.

What about actually creating the source code of a function at run-time
and compiling it?

  (let ((source-code (list 'lambda (list 'x 'y) ...)))
    (compile nil source-code))

Here, we are applying the compiler (available at run-time) to syntax
which represents a function. The compiler analyzes the syntax and
compiles the function for us, giving us an object that can be called.

Without that syntax which can represent a function, what do you pass to
the compiler?

If we didn't have lambda in Lisp, we could still take advantage of the
fact that the compiler can also take an interpreted function object and
compile that, rather than source code. So we could put together an
expression which looks like this:

  (flet ((some-name (x y) ...)) #'some-name)

We could EVAL this expression, which would give us a function object,
which can then be passed to COMPILE. So we have to involve the
evaluator in addition to the compiler, and it only works because the
compiler is flexible enough to accept function objects in addition to
source code.

> No; lambda is a bit more convenient.  But this doesn't seem like a very
> big issue worth a flame war.  If GvR says multi-line lambda would make
> the lexer more complicated and he doesn't think it's worth all the effort,
> I don't see any need to argue about it.

I.e. GvR is the supreme authority. If GvR rationalizes something as
being good for himself, that's good enough for me and everyone else.

> I won't say more, since Alex Martelli already pointed out that Google is
> doing big things with Python and it seems to scale well for them.

That's pretty amazing for something that doesn't even have a native
compiler, and big mutexes in its intepreter core.

Look at "docs.python.org" in section 8.1 en titled "Thread State and
the Global Interpreter Lock":

"The Python interpreter is not fully thread safe. In order to support
multi-threaded Python programs, there's a global lock that must be held
by the current thread before it can safely access Python objects.
Without the lock, even the simplest operations could cause problems in
a multi-threaded program: for example, when two threads simultaneously
increment the reference count of the same object, the reference count
could end up being incremented only once instead of twice. Therefore,
the rule exists that only the thread that has acquired the global
interpreter lock may operate on Python objects or call Python/C API
functions. In order to support multi-threaded Python programs, the
interpreter regularly releases and reacquires the lock -- by default,
every 100 bytecode instructions (this can be changed with
sys.setcheckinterval())."

That doesn't mean you can't develop scalable solutions to all kinds of
problems using Python. But it does mean that the scalability of the
overall solution comes from architectural details that are not related
to Python itself. Like, say, having lots of machines linked by a fast
network, working on problems that decompose along those lines quite
nicely.




More information about the Python-list mailing list