PEP 255: Simple Generators
Tim Peters
tim.one at home.com
Fri Jun 22 17:22:14 EDT 2001
[Denys Duchier]
> Think for example of a tree traversal algorithm that is parametrized
> by a procedure to be applied to each node. My reading of the PEP does
> not allow a procedure to be passed in that `yields' the node.
The PEP gives two examples (one recursive, one not, but the distinction is
internal implementation detail) of inorder node generators for a specific
kind of binary tree.
def generic_processor(traversable_thing, function_to_apply):
for node in traversable_thing:
function_to_apply(node)
works fine, and isn't limited to trees -- traversable_thing can be any
iterable object. Call via, e.g. (using the PEP example's names)
generic_processor(t, some_leaf_function)
where t is an instance of the PEP's Tree class.
If you mean instead that you want to be able to pass a procedure to the tree
traverser itself, fine, but then there's no real attraction to generators at
all.
> While possibly aberrant, let me suggest a higher-order programming
> approach that would not even require a new keyword but merely a new
> primitive which I'll call `generator'. This primitive takes a unary
> procedure as argument and returns an iterator. The unary procedure
> takes itself a `yield' procedure as argument.
You do know that most calculus students drop out at "for every epsilon > 0
there exists a delta such that for every x where ..." <wink>.
> Here is the fib example of the PEP written with my proposal:
>
> def fib(yield):
> a, b = 0, 1
> while 1:
> yield(b)
> a, b = b, a+b
>
> g=generator(fib)
I'm guessing at what all these pieces mean, but the final clue is missing:
what do I do with g? Like, for example,
for value in g:
print value
? If so, how is Jython supposed to implement this? As the PEP says,
"yield" is a keyword in large part because the Jython implementors can't
alter the JVM, and they believe they need to know all potential suspension
points at compile-time (implementing this stuff on top of Java threads
instead is not a practical option).
I would also miss the ability to pass arguments to generators in a natural
way (I know I can fake it by mucking with lexical closures to make the
function actually passed "look like" a one-argument function, but that's a
strain in Python too).
> It's straightforward (in an implementation) to check that the yield
> procedure which is invoked actually corresponds to the generator that
> is currently executing. If that's not the case, an exception should
> be raised.
>
> This proposal has the advantage that the boundary of the generator is
> fixed late, namely at the time the `generator' primitive is invoked.
> It also support full compositionality.
I'm not sure what you mean by that, but if it implies that you want, e.g.,
to be able to, within fib, pass yield on to a *called* function bif(yield),
and also yield values from within bif, then we need the Stackless Python
machinery to support that. PEP 255 is strictly a "one-frame gimmick": a
255 generator need only remember the state of a single frame, and can only
yield to the generator's immediate invoker. Since the PVM is currently
written in C using recursion, anything more general than that creates severe
implementation difficulties.
> ...
> which I would write as follows:
>
> def zipgen(g,h):
> def result(yield):
> g(yield)
> h(yield)
> return result
We can't support that, for reasons sketched in the preceding paragraph:
when the guts of g decide to yield, we have a (recursive) C stack frame,
inside the PVM, "stuck in the middle" between the invocation of "result" and
the invocation of g. In order for "result"'s invoker to regain control, the
C stack has to be popped. Icon (or at least one implementation I used for
several years) manages to do this via platform-specific assembler to do
cactus-stack-like context switches fooling the platform C implementation,
and Stackless Python does it via eliminating C recursion from the PVM's eval
loop (it supports full-blown continuations).
The "Simple" in "Simple Generators" sidesteps all of that by supporting only
generator->immediate_caller yields; but, as the recursive "inorder" PEP
example shows, that's less a limitation than it may first appear (and Icon
has the same limitation; the "cactus-stack-like" stuff is required only for
Icon co-expressions, which are consequently not supported on all platforms).
More information about the Python-list
mailing list