[Python-Dev] Another Anonymous Block Proposal

2005-04-27 Thread Jason Diamond
Hi.
I hope you don't mind another proposal. Please feel free to tear it apart.
A limitation of both Ruby's block syntax and the new PEP 340 syntax is 
the fact that they don't allow you to pass in more than a single 
anonymous block parameter. If Python's going to add anonymous blocks, 
shouldn't it do it better than Ruby?

What follows is a proposal for a syntax that allows passing multiple, 
anonymous callable objects into another callable. No new protocols are 
introduced and none of it is tied to iterators/generators which makes it 
much simpler to understand (and hopefully simpler to implement).

This is long and the initial syntax isn't ideal so please bear with me 
as I move towards what I'd like to see.

The Python grammar would get one new production:
   do_statement ::=
   "do" call ":" NEWLINE
   ( "with" funcname "(" [parameter_list] ")" ":" suite )*
Here's an example using this new "do" statement:
   do process_file(path):
   with process(file):
   for line in file:
   print line
That would translate into:
   def __process(file):
   for line in file:
   print line
   process_file(path, process=__process)
Notice that the name after each "with" keyword is the name of a 
parameter to the function being called. This will be what allows 
multiple block parameters.

The implementation of `process_file` could look something like:
   def process_file(path, process):
   try:
   f = file(path)
   process(f)
   finally:
   if f:
   f.close()
There's no magic in `process_file`. It's just a function that receives a 
callable named `process` as a parameter and it calls that callable with 
one parameter.

There's no magic in the post-translated code, either, except for the 
temporary `__process` definition which shouldn't be user-visible.

The magic comes when the pre-translated code gets each "with" block 
turned into a hidden, local def and passed in as a parameter to 
`process_file`.

This syntax allows for multiple blocks:
   do process_file(path):
   with process(file):
   for line in file:
   print line
   with success():
   print 'file processed successfully!'
   with error(exc):
   print 'an exception was raised during processing:', exc
That's three separate anonymous block parameters with varying number of 
parameters in each one.

This is what `process_file` might look like now:
   def process_file(path, process, success=None, error=None):
   try:
   try:
   f = file(path)
   process(f)
   if success:
   success(()
   except:
   if error:
   error(sys.exc_info())
   raise
   finally:
   if f:
   f.close()
I'm sure that being able to pass in multiple, anonymous blocks will be a 
huge advantage.

Here's an example of how Twisted might be able to use multiple block 
parameters:

   d = do Deferred():
   with callback(data): ...
   with errback(failure): ...
(After typing that in, I realized the do_statement production needs an 
optional assignment part.)

There's nothing requiring that anonymous blocks be used for looping. 
They're strictly parameters which need to be callable. They can, of 
course, be called from within a loop:

   def process_lines(path, process):
   try:
   f = file(path)
   for line in f:
   process(line)
   finally:
   if f:
   f.close()
   do process_lines(path):
   with process(line):
   print line
Admittedly, this syntax is pretty bulky. The "do" keyword is necessary 
to indicate to the parser that this isn't a normal call--this call has 
anonymous block parameters. Having to prefix each one of these 
parameters with "with" is just following the example of "if/elif/else" 
blocks. An alternative might be to use indentation the way that class 
statements "contain" def statements:

   do_statement ::=
   "do" call ":" NEWLINE
   INDENT
   ( funcname "(" [parameter_list] ")" ":" suite )*
   DEDENT
That would turn our last example into this:
   do process_lines(path):
   process(line):
   print line
The example with the `success` and `error` parameters would look like this:
   do process_file(path):
   process(file):
   for line in file:
   print line
   success():
   print 'file processed successfully!'
   error(exc):
   print 'an exception was raised during processing:', exc
To me, that's much easier to see that the three anonymous block 
statements are part of the "do" statement.

It would be ideal if we could even lose the "do" keyword. I think that 
might make the grammar ambiguous, though. If it was possible, we could 
do this:

   process_file(path):
   process(file):
   for line in file:
   print line
   success():
   print 'file processed successfully!'
   error(exc):

Re: [Python-Dev] Another Anonymous Block Proposal

2005-04-27 Thread Jason Diamond
Paul Svensson wrote:
 You're not mentioning scopes of local variables, which seems to be
 the issue where most of the previous proposals lose their balance
 between hairy and pointless...
My syntax is just sugar for nested defs. I assumed the scopes of local 
variables would be identical when using either syntax.

Do you have any pointers to that go into the issues I'm probably missing?
Thanks.
--
Jason
___
Python-Dev mailing list
[email protected]
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com