Re: [Python-Dev] Anonymous blocks: Thunks or iterators?

2005-04-29 Thread Brian Sabbey
Jim Jewett wrote:
The only members that need special attention are (f_code, f_lasti)
and possibly (f_blockstack, f_iblock).
You don't even need to take care of f_code.  The thunk and its surrounding 
function can share the same code.  The thunk gets compiled into the 
function the same way the body of a for loop would.

(f_code, f_lasti) would need to be replaced with a stack of pairs.
Finishing a code string would mean popping this stack, rather
than popping the whole frame.
There doesn't need to be a stack; each thunk can store its own f_lasti.
One also needs to store f_back, and, to avoid exception weirdness, 
f_exc_XXX.

In this way, calling the thunk is much like resuming a generator.
-Brian
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Anonymous blocks: Thunks or iterators?

2005-04-28 Thread Brian Sabbey
Guido van Rossum wrote:
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.
I like PEP 340 a lot, probably as much or more than any thunk ideas I've 
seen.  But I want to defend thunks here a little.

It is possible to implement thunks without them creating their own frame. 
They can reuse the frame of the surrounding function.  So a new frame does 
not need to be created when the thunk is called, and, much like with a 
yield statement, the frame is not taken down when the thunk completes 
running.  The implementation just needs to take care to save and restore 
members of the frame that get clobbered when the thunk is running.

Cells would of course not be required if the thunk does not create its own 
frame.

The main advantage of thunks that I can see is that you can save the
thunk for later, 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.
I would also be uncomfortable if the thunk could be called at a later 
time.  This can be disallowed by raising an exception if such an attempt 
is made.

Such a restriction would not be completely arbitrary.  One consequence of 
having the thunk borrow its surrounding function's frame is that it does 
not make much sense, implementationally speaking, to allow the thunk to be 
called at a later time (although I do realize that it's difficult to 
implement is not a good argument for anything).

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 :-).
If it is accepted that the thunk won't be callable at a later time, then I 
think it would seem normal that a return statement would return from the 
surrounding function.

-Brian
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] anonymous blocks

2005-04-21 Thread Brian Sabbey
Greg Ewing wrote:
I also have a thought concerning whether the block
argument to the function should come first or last or
whatever. My solution is that the function should take
exactly *one* argument, which is the block. Any other
arguments are dealt with by currying. In other words,
with_file above would be defined as
 def with_file(filename):
   def func(block):
 f = open(filename)
 try:
   block(f)
 finally:
   f.close()
   return func
This would also make implementation much easier. The
parser isn't going to know that it's dealing with anything
other than a normal expression statement until it gets to
the 'as' or ':', by which time going back and radically
re-interpreting a previous function call could be awkward.
I made an example implementation, and this wasn't an issue.  It took some 
code to stick the thunk into the argument list, but it was pretty 
straightforward.  The syntax that is actually used by the parser can be 
the same regardless of whether or not argument list augmentation is done, 
so the parser will not find one more awkward than the other.

This way, the syntax is just
 expr ['as' assignment_target] ':' suite
and the expr is evaluated quite normally.
Requiring arguments other than the block to be dealt with by currying can 
lead to problems.  I won't claim these problems are serious, but they will 
be annoying.  Say, for example, you create a block-accepting function that 
takes no arguments.  Naturally, you would define it like this:

def f(block):
do_something_with_block
Now, say you want to add to this function an optional argument, so you 
wrap another function around it like in your 'with_file' example above. 
Unfortunately, now you need to go find every call of this function and add 
empty parentheses.  This is annoying.  Remember the first time you added 
optional arguments to a function and what a relief it was not to have to 
go find every call to that function and stick in the extra argument? 
Those days are over!  (well, in this case anyway.)

Some people, aware of this problem of adding optional arguments, will 
define *all* of their block-accepting functions so that they are wrapped 
in another function, even if that function takes no arguments (and wars, 
annoying ones, will be fought over whether this is the right way to do 
it or not!):

def f():
def real_func(block):
pass
return real_func
Now the documentation gets confusing.  Just saying that the function 
doesn't take any non-block arguments isn't enough.  You would need very 
specific language, which many library authors will not provide.

And there will always be that extra step in thought: do I need the stupid 
parentheses or not?  There will inevitably be people (including me) who 
get the parentheses wrong because of absentmindedness or carelessness. 
This will be an extra little speed bump.

Now, you may say that all these arguments apply to function decorators, so 
why have none of these problems appeared?  The difference is that defining 
a function takes a long time, so a little speed bump when decorating it 
isn't a big deal.  But blocks can be defined almost instantly.  Much of 
their purpose has to do with making things quicker.  Speed bumps are 
therefore a bigger deal.

This will also be an issue for beginners who use python.  A beginner won't 
necessarily have a good understanding of a function that returns a 
function.  But such an understanding would be required simply to *use* 
block-accepting functions.  Otherwise it would be completely mysterious 
why sometimes one sees this

f(a,b,c) as i:
pass
and sometimes this
g as i:
pass
even though both of these cases just seem to call the function that 
appears next to 'as' (imagine you don't have the source of 'f' and 'g'). 
Even worse, imagine finally learning the rule that parentheses are not 
allowed if there are zero arguments, and then seeing:

h() as i:
pass
Now it would just seem arbitrary whether or not parentheses are required 
or disallowed.  Such an issue may seem trivial to an experienced 
programmer, but can be very off-putting for a beginner.

Another set of question arose for me when Barry started musing over the 
combination of blocks and decorators.  What are blocks?  Well, obviously 
they are callable.  What do they return?  The local namespace they 
created/modified?
I think the return value of a block should be None.
In constructs like with_file, the block is being used for
its side effect, not to compute a value for consumption
by the block function. I don't see a great need for blocks
to be able to return values.
If you google filetype:rb yield, you can see many the uses of yield in 
ruby.  By looking for the uses in which yield's return value is used, you 
can find blocks that return values.  For example, t = yield() or unless 
yield() indicate that a block is returning a value.  It is true that most 
of the time blocks do not return values, but 

Re: [Python-Dev] anonymous blocks

2005-04-19 Thread Brian Sabbey
Shannon -jj Behrens wrote:
Have you guys considered the following syntax for anonymous blocks?  I
think it's possible to parse given Python's existing syntax:
  items.doFoo(
  def (a, b) {
  return a + b
  },
  def (c, d) {
  return c + d
  }
  )
There was a proposal in the last few days on comp.lang.python that allows 
you to do this in a way that requires less drastic changes to python's 
syntax.  See the thread pre-PEP: Suite-Based Keywords (shamless plug) 
(an earlier, similar proposal is here: 
http://groups.google.co.uk/groups?selm=mailman.403.1105274631.22381.python-list 
%40python.org ).

In short, if doFoo is defined like:
def doFoo(func1, func2):
pass
You would be able to call it like:
doFoo(**):
def func1(a, b):
return a + b
def func2(c, d):
return c + d
That is, a suite can be used to define keyword arguments.
-Brian
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] anonymous blocks

2005-04-19 Thread Brian Sabbey
Guido van Rossum wrote:
See the thread pre-PEP: Suite-Based Keywords (shamless plug)
(an earlier, similar proposal is here:
http://groups.google.co.uk/groups?selm=mailman.403.1105274631.22381.python-list
%40python.org ).
In short, if doFoo is defined like:
def doFoo(func1, func2):
pass
You would be able to call it like:
doFoo(**):
def func1(a, b):
return a + b
def func2(c, d):
return c + d
That is, a suite can be used to define keyword arguments.
I'm still not sure how this is particularly solving a pressing problem
that isn't solved by putting the function definitions in front of the
call. I saw the first version of the proto-PEP and didn't think that
the motivating example (keeping the getx/setx methods passed to a
property definition out of the class namespace) was all that valuable.
OK.  I think most people (myself included) who would prefer to define 
properties (and event handlers, etc.) in this way are motivated by the 
perception that the current method is just ugly.  I don't know that it 
solves any pressing problems.

Two more issues:
(1) It seems that *every* name introduced in the block automatically
becomes a keyword argument. This looks like a problem, since you could
easily need temporary variables there. (I don't see that a problem
with class bodies because the typical use there is only method and
property definitions and the occasional instance variable default.)
Combining the suite-based keywords proposal with the earlier, 'where' 
proposal (linked in my above post), you would be able to name variables 
individually in the case that temporary variables are needed:

f(x=x):
x = [i**2 for i in [1,2,3]]
(2) This seems to be attaching a block to a specific function call but
there are more general cases: e.g. you might want to assign the return
value of doFoo() to a variable, or you might want to pass it as an
argument to another call.
The 'where' proposal also doesn't have this problem.  Any expression is 
allowed.

*If* we're going to create syntax for anonymous blocks, I think the
primary use case ought to be cleanup operations to replace try/finally
blocks for locking and similar things. I'd love to have syntactical
support so I can write
blahblah(myLock):
   code
   code
   code
instead of
myLock.acquire()
try:
   code
   code
   code
finally:
   myLock.release()
Well, that was my other proposal, pre-PEP: Simple Thunks (there is also 
an implementation).  It didn't seem to go over all that well.  I am going 
to try to rewrite it and give more motivation and explanation (and maybe 
use 'with' and 'from' instead of 'do' and 'in' as keywords).

-Brian
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] anonymous blocks

2005-04-19 Thread Brian Sabbey
Guido van Rossum wrote:
@acquire(myLock):
code
code
code
It would certainly solve the problem of which keyword to use! :-) And
I think the syntax isn't even ambiguous -- the trailing colon
distinguishes this from the function decorator syntax. I guess it
would morph '@xxx' into user-defined-keyword.
How would acquire be defined? I guess it could be this, returning a
function that takes a callable as an argument just like other
decorators:
def acquire(aLock):
   def acquirer(block):
   aLock.acquire()
   try:
   block()
   finally:
   aLock.release()
   return acquirer
and the substitution of
@EXPR:
   CODE
would become something like
def __block():
   CODE
EXPR(__block)
Why not have the block automatically be inserted into acquire's argument 
list?  It would probably get annoying to have to define inner functions 
like that every time one simply wants to use arguments.  For example:

def acquire(block, aLock):
aLock.acquire()
try:
block()
finally:
aLock.release()
@acquire(myLock):
code
code
code
Of course, augmenting the argument list in that way would be different 
than the behavior of decorators as they are now.

-Brian
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] Re: Re: anonymous blocks

2005-04-19 Thread Brian Sabbey
Fredrik Lundh wrote:
Brian Sabbey wrote:
If suites were commonly used as above to define properties, event handlers 
and other callbacks, then I think most people would be able to comprehend 
what the first example above is doing much more quickly than the second.
wonderful logic, there.  good luck with your future adventures in language 
design.

/F
I'm just trying to help python improve.  Maybe I'm not doing a very good 
job, I don't know.  Either way, there's no need to be rude.

If I've broken some sort of unspoken code of behavior for this list, then 
maybe it would be easier if you just 'spoke' it (perhaps in a private 
email or in the description of this list on python.org).

I'm not sure what your point is exactly.  Are you saying that any language 
feature that needs to be commonly used to be comprehendible will never be 
comprehendible because it will never be commonly used?  If so, then I do 
not think you have a valid point.  I never claimed that keyword suites 
*need* to be commonly used to be comprehendible.  I only said that if they 
were commonly used they would be more comprehendible than the alternative. 
I happen to also believe that seeing them once or twice is enough to make 
them about equally as comprehendible as the alternative.

-Brian
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


[Python-Dev] thunks (was: code blocks using 'for' loops and generators)

2005-03-16 Thread Brian Sabbey
Jim Jewett wrote:
It may be time to PEP (or re-PEP), if only to clarify what people are
actually asking for.
I will PEPify this, unless someone does not think I am the correct person 
to do so.  The PEP is probably a better place to try to address questions 
you raise, as well as give the rationale that Josiah Carlson was looking 
for.

But, in short:
Brian Sabbey's example from message
http://mail.python.org/pipermail/python-dev/2005-March/052202.html
*seems* reasonably clear, but I don't see how it relates in any way to
for loops or generators, except as one (but not the only) use case.
The original post in this thread was an idea about using 'for' loops and 
generators, but that idea has since been replaced with something else.

(1)  Calls for Ruby blocks or thunks are basically calls for
placeholders in a function.  These placeholders will be filled
with code from someplace else, but will execute in the function's
own local namespace.
It wasn't my intention that the thunk would execute in the function's 
namespace (function here is to mean the function that takes the thunk as 
an argument).  I was thinking that scope rules for the thunk would mimic 
the rules for control flow structures.

-Brian
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] code blocks using 'for' loops and generators

2005-03-15 Thread Brian Sabbey
Samuele Pedroni wrote:
My point is that a suite-based syntax
can only be a half substitute for lambda and anyway requiring a suite
seems overkill and unnatural for the just 1 expression case, for example
predicates. IOW a suite-based syntax is not a lambda killer in itself, I
would not try to stress that point.
I see your point (also I see Greg Ewing's related point).
multiple dispatch has nothing to do with syntax, in fact usual call
syntax is sufficient, and people do use multiple dispatch sometimes,
and decorators now can be even used to sugar up the definition side
of it.
But one needs to use decorators or some other mechanism for the sugar, 
that is all I intended the phrase does not give syntactic support to 
mean.  Perhaps syntactic sugar is the correct term to have used.

for something that would be rarely used, I do not think
well that's up to discussion to discover
ok
well, but this is stated without even trying to come up with a syntax
for that case. Notice that the first time around Guido himself would
have preferred if achievable a multithunk syntax, he obviously can have
changed his mind. But, yes, syntax vs expressivity is the key issue here.
Ok.  Allow me to try.  Up to a choice of (or existence of!) keywords, the 
simplest to me is:

def add(thunk1, thunk2, other):
print thunk1(1,2) + thunk2(3,4) + other
with x,y from add(100):
value x*y
also a,b:   # yikes??
value a*b   # this is thunk2
-Brian
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] code blocks using 'for' loops and generators

2005-03-14 Thread Brian Sabbey
be guaranteed to run under all conditions, I think it
would be useful if it could be arranged so that
 for x in somegenerator():
   ...
   raise Blather
   ...
would caused any finallies that the generator was suspended
inside to be executed. Then the semantics would be the
same as if the for-loop-over-generator were implemented by
passing a thunk to a function that calls it repeatedly.
One difficulty is that one can never know if the user intends to still use 
the generator, like so:

a = somegenerator()
try:
for x in a:
raise Blather
except:
a.next()
I think they only way you can really be sure .next() will not be called 
again is if the generator is no longer referenced.  Someone submitted a 
patch once to execute finallies when the generator is __del__eted, but 
it was rejected for various reasons.

In my original post in this thread I tried to provide a mechanism such as 
you describe by providing __call__ as an alternative to 'next', but now I 
am convinced that it is better to introduce a new syntax instead of 
re-using generators.

Incidentally, passing the thunk behind the scenes as the first argument 
(as mentioned previously) allows one to avoid using lambda to do things 
such as sort (I hear lambdas are on the way out), while remaining 
anonymous:

with x, y from a.sort():
value cmp(x.k1, y.k1) or (x.k2, y.k2)
(or whatever the preferred syntax is) instead of:
a.sort(lambda x,y : cmp(x.k1, y.k1) or (x.k2, y.k2))
Not that I find either form better than the other, but I do find both 
better than have to define a named function.

I am going to see if I can make a PEP for this.
-Brian
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] code blocks using 'for' loops and generators

2005-03-14 Thread Brian Sabbey
Samuele Pedroni wrote:
OTOH a suite-based syntax for thunks can likely not work as a substitute of 
lambda for cases like:

f(lambda: ..., ...)
where the function is the first argument, and then there are further 
arguments.
I do not see why you say suite-based thunks cannot be used in the case in 
which the function takes more than one argument.  Using the syntax I 
described earlier, they work in just that way:

def pickled_file(thunk, name):
...
new_data = thunk(pickle.load(f))
...
with greetings from pickled_file('greetings.pickle'):
...
value greetings
One can make an analogy with self.  Both the thunk and self can be 
passed automatically as the first argument, and further arguments can 
follow.  In this way, functions that already take a thunk as a first 
argument (such as sort) can be re-used without modification.

Apart this one very hard problem, it would be nice to be able to define
and pass more thunks simultaneously. In particular a more concise syntax for
 def setx(self, v): self._x = v
 def getx(self): return self._x
 x = property(getx,setx)
was considered in the past discussions about the topic a worthy goal.
Here I can make another analogy with self.  Just as python does not give 
syntactic support for multiple dispatch because (I assume) it would 
require major syntax changes for something that would be rarely used, I do 
not think it is worth it to give syntactic support for multiple thunks. 
If only a fraction epsilon of functions take a single thunk, then I 
would guess that epsilon**2 take two thunks, and it is not worth 
creating syntax for such a small number of cases, especially if that 
syntax compromises what would otherwise be a much cleaner syntax for the 
single thunk case.

-Brian
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] code blocks using 'for' loops and generators

2005-03-13 Thread Brian Sabbey
On Sun, 13 Mar 2005, Greg Ewing wrote:
Brian Sabbey wrote:
I prefer re-using the 'for' loop for this purpose because it allows the 
problem to be solved in a general way by re-using a structure with which 
most users are already familiar, and uses generators, which are easier to 
use in this case than defining a class with __exit__, __enter__, etc.
But this is an abuse of both the for-loop and the generator.
It's using a for-loop for something that does not loop and
is never intended to loop, and it's using yield to do
something other than producing a value for consumption.
I'd rather see a new mechanism altogether for this very
different use case.
The problem with creating a new mechanism is that sometimes you will want 
to loop.  For example, reading a bunch of items from a shared resource, 
modifying them, and sending them back.  A new, non-looping mechanism will 
not be adequate for this because it cannot loop, and a 'for' loop will not 
be adequate for the same reason it is not adequate now: it can't 
automatically unlock the resource at the end of the loop.

I was looking for a single mechanism to handle all of these cases, but I 
can see why you and most people would not be so keen on the idea of 
abusing (I prefer expanding the uses of) 'for' loops this way.

-Brian
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] code blocks using 'for' loops and generators

2005-03-13 Thread Brian Sabbey
On Mon, 14 Mar 2005, Greg Ewing wrote:
Brian Sabbey wrote:
The problem with creating a new mechanism is that sometimes you will want 
to loop.  For example, reading a bunch of items from a shared resource, 
modifying them, and sending them back.  A new, non-looping mechanism will 
not be adequate for this because it cannot loop,
If there is a mechanism for passing a code block as
a thunk to an arbitrary function, the function is
free to loop or not as it sees fit. I'd just prefer
the spelling of it didn't force you to make it
look like it's looping when it's not.
I think you're right.  How about something like below?  In the same way 
that self is passed behind the scenes as the first argument, so can 
the thunk be.  (Many ideas taken from around [1])

def stopwatch(thunk):
t = time.time()
thunk()
return t - time.time()
with stopwatch() result dt:
a()
b()
print 'it took', dt, 'seconds to compute'
=
def pickled_file(thunk, name):
f = open(name)
new_data = thunk(pickle.load(f))
f.close()
f = open(name, 'w')
pickle.dump(new_data, f)
f.close()
with greetings from pickled_file('greetings.pickle'):
greetings.append('hello')
greetings.append('howdy')
value greetings
[1] http://mail.python.org/pipermail/python-dev/2003-February/032732.html
-Brian
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] code blocks using 'for' loops and generators

2005-03-12 Thread Brian Sabbey
On Fri, 11 Mar 2005, Josiah Carlson wrote:
My first reaction to the proposal was ick.  Why?  Because every time
I've seen a mechanism for modifying the internals of generators
constructed using yield, the syntax has been awful; in my opinion
(whether my opinion means anything is another matter), the syntax you
propose is quite awful,
I find it quite natural.  Stuff on the right of 'yield' is going out, 
stuff on the left is coming in.  Since 'yield' is different than return in 
that it marks a spot at which execution both leaves and re-enters the 
frame, it makes sense that 'yield' should have a syntax that indicates as 
much.

and the functionality you desire does not
require syntax modification to be possible.
Was the functionality provided by generators or decorators or anything 
else impossible before those were introduced?  Of course not.  The point 
is to make things easier and more natural, not to enable some previously 
impossible functionality.

Strawman 1: the syntax
def pickled_file(name):
f = open(name, 'r')
l yield pickle.load(f)
^
--
|f.close()
|f = open(name, 'w')
|pickle.dump(l, f)
|f.close()
|
While this is currently a syntax error, it is not clear that an
assignment is happening /at all/, and I don't believe it would be /even
if/ if were documented, and every time someone opened up Python it said
This is an assignment! with an example. It looks too magical to me
(and from a guy who had previously proposed 'Some' as the opposite of
'None', that's saying something).
Perhaps you are right, I don't know.  It seems to me that most people 
would have to see the syntax once to know exactly what is going on, but I 
certainly don't know that for sure.  Either way, I'd hate to have all my 
suggestions dismissed because of the syntax of this one piece.

Strawman 2: putting data back into a generator
def pickled_file(name):
f = open(name, 'r')
yield pickle.load(f)
f.close()
f = open(name, 'w')
pickle.dump(l, f)
f.close()
Keep your code, except toss that leading 'l'.
for l in pickled_file('greetings.pickle'):
l.append('hello')
l.append('howdy')
Toss that 'continue l', and your code will work (as long as both the
function and the for are sitting in the same namespace).
But they're *not* in the same namespace necessarily.  That is entirely the 
point.  One is changing scope but has no clean way to pass values.  How is 
making 'l' some (more) global variable possibly a clearer way to pass it 
to the generator?  Your argument is like saying one does not need to 
return values from a function because we could always just use a global 
variable to do it.

Hrm, not good enough?  Use a Queue, or use another variable in a
namespace accessable to both your function and your loop.
Again, I entirely realize it's possible to do these things now, but that 
is not the point.

I'm sorry if this email comes off harsh,
I'm just relieved someone responded :)
-Brian
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] code blocks using 'for' loops and generators

2005-03-12 Thread Brian Sabbey
On Sat, 12 Mar 2005, Steven Bethard wrote:
The goals behind this seem a lot like the goals of PEP 288[1].  I
remember discussions suggesting code like:
def gen():
   a, b, c=3 = yield 1
   yield a + b*c
g = gen()
print g.next() # prints 1
print g.next(1, 2) # prints 7
But as you can see, this creates a weird asymmetry because the last
yield throws away its arguments, and depending on how the generator is
written, different calls to next may require a different number of
arguments.  This means that, unless the code is extremely well
documented, you have to read the source code for the generator to know
how to call it.
The intention of my proposal was for using generators with 'for' loops. 
In this case, the generator runs to completion, so the arguments to the 
last yield are never thrown away.  If 'next' were not able to take any 
arguments, that would be compatible with my proposal.

Also, there was the issue that there is an asymmetry because the first 
call to 'next' does not take any arguments.  This asymmetry does not 
exist, however, when using the generator in a 'for' loop, because there is 
no first call to 'continue' in such a case.

Because of these and other complications, I believe the PEP is now
lobbying for a way to get the generator instance object and a way to
cause an exception to be thrown from outside the generator.  Take a
look and see if the PEP might meet your needs -- I haven't seen much
action on it recently, but it seems much less invasive than your
proposal...
This PEP solves similar problems, yes.  And I would agree that my proposal 
is much more invasive on python's implementation.  From the users' point 
of view, however, I think it is much less invasive.  For example, no doubt 
there will be many users who write a generator that is to be used in a 
'for' loop and are baffled that they receive a syntax error when they try 
to write some try/finally cleanup code.  With the PEP, they would have to 
figure out that they have to use the 'throw' method of generators to 
trigger cleanup code (and then have to remember to call it each time they 
are done with the generator).  With this proposal, try/finally would just 
work as they expect and they would be non the wiser.

def pickled_file(name):
   self = mygen.get_instance()
   f = open(name, 'r')
   yield pickle.load(f)
   f.close()
   f = open(name, 'w')
   pickle.dump(self.l, f)
   f.close()

And this would be written something like:
gen = pickled_file('greetings.pickle')
for l in gen:
   l.append('hello')
   l.append('howdy')
   gen.l = l
Personally, I find this use of a generator thoroughly confusing, and I
don't see what you gain from it.  The PEP 288 examples are perhaps
somewhat more convincing though...
The disadvantage of doing it this way (or with a class wrapping the 
generator) is that it is implicit.  If I were reading the pickled_file 
code, I would have no idea where the self.l comes from.  If it is coming 
from the 'for' loop, why not just be able to explicitly say that?

I agree that this is a confusing way to use generators.  But it is the 
expected way to use code blocks as found in other languages.  It would 
take some getting used to that 'for' can be used this way, but I think it 
would be worth it.

-Brian
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] code blocks using 'for' loops and generators

2005-03-12 Thread Brian Sabbey
On Sat, 12 Mar 2005, Steven Bethard wrote:
Brian Sabbey [EMAIL PROTECTED] wrote:
I agree that this is a confusing way to use generators.  But it is the
expected way to use code blocks as found in other languages.  It would
take some getting used to that 'for' can be used this way, but I think it
would be worth it.
I guess I need some more convincing...  I don't find your approach[*], e.g.
def pickled_file(name):
   f = open(name, 'r')
   data yield pickle.load(f)
   f.close()
   f = open(name, 'w')
   pickle.dump(data, f)
   f.close()
for data in pickled_file('greetings.pickle'):
   data.append('hello')
   data.append('howdy')
   continue data
any clearer than, say:
class PickledFile(object):
def __init__(self, name):
self.name = name
f = open(self.name, 'r')
self.data = pickle.load(f)
f.close()
def close(self):
f = open(self.name, 'w')
pickle.dump(self.data, f)
f.close()
p = PickledFile('greetings.pickle')
p.data.extend(['hello', 'howdy'])
p.close()
Note that I'm not using the iteration construct (for-in) because I'm
not really doing an iteration here.  Pehaps I could be taught to read
for-in otherwise, but without an obvious benefit for doing so, I'm
really not inclined to.
In the real world, I need to lock and unlock the pickled files.  So if I 
forget to call p.close(), then it would be a problem.  I would need a 
try/finally block.  If I could use the above 'for' loop approach, I am 
able to encapsulate the try/finally code.

This is the same problem that is addressed in PEP 310.  Copied from the 
PEP, here is the basic idea:

   The syntax of a 'with' statement is as follows::
'with' [ var '=' ] expr ':'
suite
This statement is defined as being equivalent to the following
sequence of statements:
var = expr
if hasattr(var, __enter__):
var.__enter__()
try:
suite
finally:
var.__exit__()
I prefer re-using the 'for' loop for this purpose because it allows the 
problem to be solved in a general way by re-using a structure with which 
most users are already familiar, and uses generators, which are easier to 
use in this case than defining a class with __exit__, __enter__, etc.

-Brian
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


Re: [Python-Dev] code blocks using 'for' loops and generators

2005-03-12 Thread Brian Sabbey
On Sat, 12 Mar 2005, Josiah Carlson wrote:
I stand by my clever hack statement, and I will agree to disagree with 
you on it, like I agree to disagree with you about both the necessity of 
passing arbitrary values back into a generator (I think it is poor 
style, and too much brain overhead to wonder where data is coming from), 
and the necessity of a new syntax to make such things easier (if 
(ab)using generators in such a way makes /anything/ easier).
I will also agree to disagree. If I may also summarize, you see such 
syntax as magical, non-obvious and unnecessary, and I find it clean and 
useful.

-Brian
___
Python-Dev mailing list
Python-Dev@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com


[Python-Dev] code blocks using 'for' loops and generators

2005-03-11 Thread Brian Sabbey
I would like to get some feedback on a proposal to introduce 
smalltalk/ruby-like code blocks to python.  Code blocks are, among other 
things, a clean way to solve the acquire/release problem [1][2].  This 
proposal also touches on some of the problems PEP 288 [3] deals with.

The best discussion I have been able to find on this topic is in the 
thread of ref. [4].

Since generators, when used with 'for' loops, are so similar to code 
blocks [5], one can imagine two ways to implement code blocks in python: 
(1, parallel) keep them as similar as possible to 'for' loops so that a 
normal user doesn't distinguish the two, or (2, orthogonal) make them so 
much different than 'for' loops that the normal user doesn't notice the 
similarities.  Anything between these extremes will probably be confusing. 
Here I will describe an attempt at (1).

(I) Give generators a __call__ method as an alternative to 'next'. 
Method __call__ should take a single parameter: a function.  Using 
__call__ will cause the generator to start executing normally, but when a 
'yield' is reached, the generator will invoke the function passed to 
__call__ instead of activating the currently implemented 'yield' 
mechanism.

Since __call__ will be an alternative to 'next', it will raise an 
exception if 'next' has already been called and vice-versa.  Also, any 
calls to 'next' will raise an exception if there is a 'yield' in the try 
of a try/finally.  Such yields will no longer trigger a SyntaxError 
because they will not a problem when using __call__.

(II) Have a method of generators, __blockcall__, which will be equal to 
__call__, but will only exist if (1) the generator contains a try/finally 
try yield, or (2) the user explicitly defines it, for example, with a 
function decorator (@completion_required would be a descriptive name).

Have 'for' loops use __blockcall__ if it is available, and __iter__ 
otherwise.  Pass to __blockcall__ the block of code in the 'for' loop.

Scope rules for the passed block of code should mimic current 'for' loop 
behavior.  Behavior of 'break' and 'return' should be mimicked, perhaps 
with special exceptions catchable only by the 'for' loop.  Mimicking 
'yield' will be a problem unless/until multi-level yields are allowed. 
(performance and implementation difficulties for all of this?  I don't 
know).

The thunk shouldn't be savable for later use because the 'for' loop will 
no longer be around to deal with 'break' and 'return'.  This means that 
__blockcall__ will not be implementable as a function that takes a 
function as an argument.

(III) Allow 'continue' to pass values to 'yield' (something similar 
previously rejected here [6]).  As far as I know, all other control 
statements that transfer execution to a different frame (yield, return, 
raise) pass values, and I don't see why this case should be any different. 
I do not see such a mechanism as gimmicky;  being able to cleanly pass 
values when changing scope is an inherent part of nearly every programming 
language.

As an example of the syntax I am suggesting, here is something I was 
desiring recently, a generator to open, unpickle, repickle and close a 
file:

def pickled_file(name):
f = open(name, 'r')
l yield pickle.load(f)
f.close()
f = open(name, 'w')
pickle.dump(l, f)
f.close()
The unpickled object is sent to the caller at the yield statement, and the 
modified object is received back at the same statement.  Note the 
suggested 'yield' syntax and the conspicuous absence of '='.  This syntax 
is backwardly compatible with current yield syntax.  Also, this syntax 
does not require yield to appear as a function; it is still clear that 
this is a unique control-flow statement.

This function would be used like this:
for l in pickled_file('greetings.pickle'):
l.append('hello')
l.append('howdy')
continue l
The above code would have the same effect as:
def named(l):
l.append('hello')
l.append('howdy')
return l
pickled_file('greetings.pickle')(named)
(IV)  Allow 'yield' to return no value;  in this case a new keyword, 
'with', will be required instead of an awkward 'for':

with f():   instead of   for in f():
(V)  For the same reasons as in (III), allow generators to return values. 
These values can be sent with the StopIteration exception if 'next' is 
being used for iteration.  An obvious syntax for receiving these values is 
shown by this example:

with dt = stopwatch():
  f()
  g()
print 'it took', dt, 'seconds'
Although with stopwatch() result dt: might not be so bad.
[1] PEP 310 Reliable Acquisition/Release Pairs
http://www.python.org/peps/pep-0310.html
[2] PEP 325 Resource-Release Support for Generators
http://www.python.org/peps/pep-0325.html
[3] PEP 288 Generators Attributes and Exceptions
http://www.python.org/peps/pep-0288.html
[4] http://mail.python.org/pipermail/python-dev/2003-February/032800.html
[5]