Re: lambda constructions

graham@laplace.anu.edu.au
Tue, 31 Jan 95 14:19:55 +1100

>> I was wondwering about the 'lambda' construction in Python. It seems to
>> me thats its semantics are backwards. Say I do something like this,
>>
>> n = 1
>> f = lambda x: x + n
>>
>> I would expect f to be the function f(x)=x+1. This is how a standard
>> term closure works in a functional language.
>
>Why not the function f(x)=x+n? That's how closures work in Scheme as
>far as I know. (The difference is whether a later assignment to n
>affects the meaning of f -- in Scheme it does).

Thats interesting. In ML, Haskell, Hope, etc, etc closures work as I
have described. The dialects of Scheme I have seen also do closure
forming. My guess is that for Scheme's which are tied closely
to LISP, closures work as you describe, and for those that are not
(most of the modern Scheme dialects are not tied to LISP anymore),
closures work as I describe.

My point is not really about whats standard and whats not, its rather
that I believe closures a la ML are more useful than closures a la
Scheme. I believe its more useful to be construct the function f(x)=n+1
in the above example, than the function f(x)=x+n. This is particularly
true when 'self' is involved. For example consider the following
lambda function,

Class Blah:

def __init__()
f = lambda a1, a2, a3: .... self.(....)

Now in the current Python semantics invoking f will produce an error, since
'self' cannot be found inside f. In the ML style semantics f works exactly
as desired since 'self' is substituted into f at the point of construction.
This is very useful for passing functions to other objects (eg: to tk
widgets as callback routines).

>That's gross. As a general rule, when you're applying eval() to a
>literal, there's generally a better way. (I presume you meant {'n':n}
>since as written it doesn't work.)

Yes I mean {'n':n}

>I'll show how to do it in a more Pythonic way, but first a little
>digression on scopes in Python. Python has a problem with nesting
>scopes -- it doesn't let you. This was a conscious, though not
>uncontroversial design descision.

I understand Python's scope rules and I I think they are just fine. Whether
functions form closures or not, is not really a scope issue however.

>Now, back to the big question: how to define f(x)=x+n, given a
>variable n? The simple solution is to use default arguments. This
>works (giving the first semantics, actually):
>
> f = lambda x, n=n: x+n
>
>Although it's idiomatic to use "n=n", it's perhaps clearer to write it
>like this:
>
> f = lamda x, y=n: x+y

But the problem with doing it this way is that the caller of 'f' can
over-ride the value of the 'y' argument, and I do not want to allow
this. Its unmodular and unsafe, particularly if the function has side
effects (eg. it assigns to y.some_field).

Me:
> As I think most programmers use lambda constructions as term closures,
> I would like to see the default behaviour for lambda constructions changed
> to form closures.
You:
>Two remarks here (yes I'm sligtly pissed off since this issue comes
>back time after time): (1) pulling in the rest of the world to
>strengthen your argument is a sign of weakness; (2) don't phrase your
>own frustrations or misunderstandings as language change requests.
>Sorry. That feels much better. Now on to more constructive things

I really believe this kind of comment is totally pointless. My question does
not come from my misunderstandings or frustrations. I am very happy with
Python and have said this before. I was simply attempting to post an article
which might inspire some debate on an area where I believe Python might be
improved. I have implemented several OO, and several functional languages,
and have used Python extensively these last 2 months, so my questions are not
from ignorance. If you cannot take constructive suggestions in a non personal
way, that is not my problem.

Me:
>> The solution to this problem is to use a lambda construction. Say I had
>> a set of possible event masks (a set of "<ButtonRelease-2>" specifications).
>> Then I can write this code,
>>
>> for x in self.event_masks:
>> self.tag_bind("_an_action", x,
>> eval("lambda e: e.widget.do_action(e, x)", {'x':x}))
Guido:
>This is a good point to explain why using
>
> lambda e, x=x: e.widget.do_action(e, x)

I think you are confusing the issue here. What I was suggesting was that
the code,

for x in self.event_masks:
self.tag_bind("_an_action", x, lambda e: e.widget.do_action(e,x))

with the semantics of closure forming would do the job. And it would be just
as efficient as your proposed approach.

graham

Je suis pour le communisme
Je suis pour le socialisme
Je suis pour le capitalisme
Parce que je suis opportuniste