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):