Theoretical question about Lambda

Steve Holden sholden at holdenweb.com
Tue May 7 08:25:21 EDT 2002


"Paul Foley" <see at below> wrote in message
news:m2y9ewu4fe.fsf at mycroft.actrix.gen.nz...
> 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.
>
So, it appears that you think binding is the implicit declaration assoicated
with assignment to a name inside a particular scope? If so, it would seem
particularly perverse of the implementors to have chosen the message

"Unbound local variable"

to indicate this situation. Clearly they regard the word "binding" as the
act of associating a value with a name (which is to say, making an entry
into the [admittedly conceptual] dictionary associated with a particular
namespace).

> >> 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]
>

Paul, I'm afraid Alex isn't the only one who completely fails to understand
your insistence on a distinction between binding and what you apparently
call assignment (but which Alex and I would, I suspect, simply call
rebinding).

Consider namespaces as dictionaries. I *know* they aren't all implemented as
dictionaries, but bear with me.
You seem to be insisting that an operation is only a "binding" when the key
(name) does not already exist in the dictionary (namespace). If the
dictionary (namespace) already contains the key (name) then it's not a
rebinding, it's an "assignment".

Ultimately your argument appears to be a purely semantic one, and I really
don't feel you have demonstrated why binding/rebinding is different from
binding/assignment.

you-say-assignment-i-say-tomato-ly y'rs  - steve
--
Steve Holden: http://www.holdenweb.com/ ; Python Web Programming:
http://pydish.holdenweb.com/pwp/








More information about the Python-list mailing list