[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