[Python-Dev] Anonymous blocks: Thunks or iterators?
Michael Hudson
mwh at python.net
Fri Apr 29 11:35:27 CEST 2005
Guido van Rossum <gvanrossum at gmail.com> writes:
> [Greg Ewing]
>> Elegant as the idea behind PEP 340 is, I can't shake
>> the feeling that it's an abuse of generators. It seems
>> to go to a lot of trouble and complication so you
>> can write a generator and pretend it's a function
>> taking a block argument.
>
> Maybe. You're not the first one saying this and I'm not saying "no"
> outright, but I'd like to defend the PEP.
This is kind of my point too; I'm not saying that I really prefer the
thunk solution, just that I want to see it mentioned.
I think the making-generators-more-sexy thing is nice, but I'm think
that's almost orthogonal.
[...]
> Even without a block-statement, these two changes make yield look a
> lot like invoking a thunk -- but it's more efficient, since calling
> yield doesn't create a frame.
>
> The main advantage of thunks that I can see is that you can save the
> thunk for later,
I also find them somewhat easier to understand.
> like a callback for a button widget (the thunk then becomes a
> closure). You can't use a yield-based block for that (except in
> Ruby, which uses yield syntax with a thunk-based implementation).
> But I have to say that I almost see this as an advantage: I think
> I'd be slightly uncomfortable seeing a block and not knowing whether
> it will be executed in the normal control flow or later. Defining an
> explicit nested function for that purpose doesn't have this problem
> for me, because I already know that the 'def' keyword means its body
> is executed later.
>
> The other problem with thunks is that once we think of them as the
> anonymous functions they are, we're pretty much forced to say that a
> return statement in a thunk returns from the thunk rather than from
> the containing function. Doing it any other way would cause major
> weirdness when the thunk were to survive its containing function as a
> closure (perhaps continuations would help, but I'm not about to go
> there :-).
I'm not so sure about this. Did you read this mail:
http://mail.python.org/pipermail/python-dev/2005-April/052970.html
? In this proposal, you have to go to some effort to make the thunk
survive the block, and I think if weirdness results, that's the
programmer's problem.
> But then an IMO important use case for the resource cleanup template
> pattern is lost. I routinely write code like this:
>
> def findSomething(self, key, default=None):
> self.lock.acquire()
> try:
> for item in self.elements:
> if item.matches(key):
> return item
> return default
> finally:
> self.lock.release()
>
> and I'd be bummed if I couldn't write this as
>
> def findSomething(self, key, default=None):
> block synchronized(self.lock):
> for item in self.elements:
> if item.matches(key):
> return item
> return default
If you can't write it this way, the thunk proposal is dead.
>> I'd like to reconsider a thunk implementation. It
>> would be a lot simpler, doing just what is required
>> without any jiggery pokery with exceptions and
>> break/continue/return statements. It would be easy
>> to explain what it does and why it's useful.
>
> I don't know. In order to obtain the required local variable sharing
> between the thunk and the containing function I believe that every
> local variable used or set in the thunk would have to become a 'cell'
> (our mechanism for sharing variables between nested scopes).
Yes.
> Cells slow down access somewhat compared to regular local variables.
So make them faster. I'm not sure I think this is a good argument.
You could also do some analysis and treat variables that are only
accessed or written in the block as normal locals.
This all makes a block-created thunk somewhat different from an
anonymous function, to be sure. But the creating syntax is different,
so I don't know if I care (hell, the invoking syntax could be made
different too, but I really don't think that's a good idea).
> Perhaps not entirely coincidentally, the last example above
> (findSomething() rewritten to avoid a return inside the block) shows
> that, unlike for regular nested functions, we'll want variables
> *assigned to* by the thunk also to be shared with the containing
> function, even if they are not assigned to outside the thunk. I swear
> I didn't create the example for this purpose -- it just happened.
Oh, absolutely.
>> On the other hand, a thunk implementation has the
>> potential to easily handle multiple block arguments, if
>> a suitable syntax could ever be devised. It's hard
>> to see how that could be done in a general way with
>> the generator implementation.
>
> Right, but the use cases for multiple blocks seem elusive. If you
> really want to have multiple blocks with yield, I suppose we could use
> "yield/n" to yield to the n'th block argument, or perhaps yield>>n.
> :-)
Hmm, it's nearly *May* 1... :)
Cheers,
mwh
--
I'm a keen cyclist and I stop at red lights. Those who don't need
hitting with a great big slapping machine.
-- Colin Davidson, cam.misc
More information about the Python-Dev
mailing list