atom feed25 messages in org.python.python-listTheoretical question about Lambda
FromSent OnAttachments
pekka niiranenMay 1, 2002 11:29 pm 
Erik Max FrancisMay 1, 2002 11:56 pm 
Alex MartelliMay 2, 2002 2:03 am 
Bengt RichterMay 2, 2002 9:21 am 
Alex MartelliMay 2, 2002 9:59 am 
Terry ReedyMay 2, 2002 11:11 am 
Alex MartelliMay 2, 2002 12:25 pm 
Bengt RichterMay 2, 2002 1:01 pm 
holger krekelMay 2, 2002 1:17 pm 
Steve HoldenMay 2, 2002 1:18 pm 
holger krekelMay 2, 2002 1:32 pm 
Alex MartelliMay 2, 2002 2:05 pm 
Bengt RichterMay 4, 2002 4:56 pm 
Terry ReedyMay 4, 2002 10:37 pm 
Alex MartelliMay 5, 2002 12:21 am 
Paul FoleyMay 5, 2002 10:11 pm 
Lulu of the Lotus-EatersMay 5, 2002 11:30 pm 
Alex MartelliMay 6, 2002 12:09 am 
Paul FoleyMay 6, 2002 4:20 am 
Alex MartelliMay 6, 2002 8:15 am 
Paul FoleyMay 6, 2002 6:33 pm 
Steve HoldenMay 7, 2002 5:24 am 
Alex MartelliMay 7, 2002 7:00 am 
Paul FoleyMay 7, 2002 7:03 pm 
Paul FoleyMay 7, 2002 7:14 pm 
Subject:Theoretical question about Lambda
From:Paul Foley (se@below)
Date:May 6, 2002 6:33:51 pm
List:org.python.python-list

On Mon, 06 May 2002 15:15:59 GMT, Alex Martelli wrote:

Paul Foley wrote: ...

OK; question: what's the difference, if any, between

x = n do_something

and

def foo(x): do_something

foo(n)

as far as x is concerned?

Assuming these snippets are top-level code, "the difference as far as x is concerned" is clearly that x is a global name in the first snippet, and a local name of foo in the second snippet.

And if they're not at top level? The scope difference isn't really what I was getting at.

The point is that, in the case of something like

def foo(x): bar x = 7 baz

there's only _one_ binding for x: doing "x = 7" just changes the value in the preexisting binding. That's why

def foo(x): a = lambda: x x = 7 b = lambda: x return a,b

returns two functions that both return 7; if there was a new binding after the "x = 7", the functions would return different values [assuming you don't call foo(7), of course. Also assuming nested_scopes]

In Lisp, you can do both:

(defun foo (x &aux a b) (setq a (lambda () x)) (setq x 7) ; assignment (setq b (lambda () x)) (list a b))

vs.

(defun foo (x &aux a b) (setq a (lambda () x)) (let ((x 7)) ; binding (setq b (lambda () x))) (list a b))

Note that

(let ((a1 b1) (a2 b2)) ...)

is essentially syntactic sugar for

((lambda (a1 a2) ...) b1 b2)

Lambda is how you do binding; setq can only assign into an existing binding. Same with Python's "=", except that the compiler notices any assignments and automatically adds "&aux var" to the lambda list (to put it in Lisp terms) -- and /that's/ what does the binding, not the "=", which you can easily demonstrate:

def test(): print x x = 42

raises an error because the (empty, error-causing) binding for x is already in force when the "print" statement executes, before the "=" is even reached.

Given that (lambda: x) /does/ close over x with nested scopes enabled, can you explain why

fns = getfns(seq)

produces a list of functions that all return seq[-1]? And why, as you

Because "lambda: x" is a callable that returns whatever object name x is bound to at the time it's called (rather than, at the time it's created).

Yes, but it captures the binding that's in effect at the time the function is created (i.e., when the lambda form is evaluated, not when it's eventually called). That's what "closure" means.

And that's what I'm getting at: yes, when you call the function you get whatever value is in that binding at the time of the call -- the fact that all five functions return the same value shows that they all share the same binding!

def getfns2(seq): return [(lambda x=x: x) for x in seq]

produces functions that return all the elements of seq? [Assume you call them with no arguments!]

Because "lambda x=x: x" is a callable that (when called with no arguments) returns whatever object name x was bound to at the time the callable was created (rather than, at the time it's called).

Hmmm. Does it make a difference if I write (lambda q=x: q)? It still returns whatever value the variable (now named q) is bound to at the time of the call (/not/ when it was created!), but now you have a new binding, not shared by the other functions; the value in that new binding is the value of x at the time the binding was created [and you can't change it, in Python, but that's not relevant]

Of course, you could do

def getfns2(seq): return [(lambda x: lambda: x)(x) for x in seq]

to get the same effect without the unwanted optional argument. [Again, assuming nested_scopes is enabled]

What I'm saying is that when you explain Python to people by drawing boxes for variables with arrows pointing at their values, as in the recent thread, the binding is the box, or the box-and-arrow pair, not the arrow. Assignment is making an arrow point somewhere else; binding is making a new box, with its own arrow. [As long as you don't have dynamic variables, anyway; but Python doesn't]