Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > Ruby blocks in Python, a suggestion

Reply
Thread Tools

Ruby blocks in Python, a suggestion

 
 
Ville Vainio
Guest
Posts: n/a
 
      04-07-2004
I participated in a short thread on c.l.ruby, and had myself convinced
that "blocks" might be a good idea. For bragging rights, if nothing
else .

What I'm talking about is:

<callable> block [<block args>]:
suite


<callable> would be called with one argument, 'block', which it could
execute via normal calling (or call, or execute, or whatever).

i.e.

class with_open_file:
def __init__(self, *args):
self.args = args
def __call__(self, block):
f = open(*self.args)
try:
block.call(f) # f is passed to the "block argument"
finally:
f.close()

def somefunc():
x = 10
with_open_file("a.txt","r") block(f): # assigns f to the opened file
x = len(f.read()) # rebinds x!

Note that block would be executed with the same bindings as
somefunc(), not its lexically closed bindings that would be the case
if the actions within block were in a normal function.

Other examples:

transaction(dbconn) block(t): # commit/rollback t based on exception status
t.dostuff()


foreach([(1,2), (2,4), (3,6)]) block(x,y):
assert y == X*2

mapseq(seq) block(entry, result):
result.append(entry * 2)

Obviously this functionality could be quickly hacked by making the
bindings of "block parameters" function-global just like the variable
in for loops (and LC's in the current implementation). Even if only
block-visible fucntionality would be so much cooler.

And yes, I know this blocks issue has been hashed over previously. I'm
just too busy to google for it right now, and just wanted to get this
off my chest quickly .

--
Ville Vainio http://tinyurl.com/2prnb
 
Reply With Quote
 
 
 
 
Jeff Epler
Guest
Posts: n/a
 
      04-07-2004
I'm not convinced this will fly.

First, of your examples, two are better expressed as loops or listcomps:
>
> foreach([(1,2), (2,4), (3,6)]) block(x,y):
> assert y == X*2

for x, y in [(1,2), (2,4), (3, 6)]:
assert y == x*2

> mapseq(seq) block(entry, result):
> result.append(entry * 2)


result += [entry*2 for entry in seq]

Every block that is executed N times should be expressed today as a
"for" loop or listcomp with the appropriate generator.


> transaction(dbconn) block(t): # commit/rollback t based on exception status
> t.dostuff()


I would swear there was a pep for a "with" statement, taking an object
with special methods called at the top and the bottom of the statement.
I can't find it, though. It would look something like this:
t = transaction(dbconn)
with transaction(dbconn):
t.dostuff()

Eery block that is executed exactly 1 time should be expressed today as
t.acquire()
try:
block
finally:
t.release()
or
t.acquire()
try:
block
except:
t.rollback()
raise
else:
t.commit()


The second problem is that I don't know if the Python parser can
accomodate your syntax. Your proposal doesn't mention the exact
production(s) you would add or change to add your block statement.

One way would be to make compound_stmt include a block_stmt alternative
compound_stmt: block_stmt | if_stmt | ...
block_stmt: test 'block' ['(' arglist ')'] ':' suite
can you tell me if this is the way you imagined defining the new rule,
and show me that this does not create a conflicting or ambiguous
grammar? (the fact that Python prints "XXX ambiguity!" over and over
again when I try to build the modified grammar doesn't bode well, but
maybe it's just been too long since I toyed with the Python grammar..)

Finally, what do "break" and "continue" do in blocks?

Jeff

 
Reply With Quote
 
 
 
 
John Roth
Guest
Posts: n/a
 
      04-07-2004
"Ville Vainio" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed)...
> I participated in a short thread on c.l.ruby, and had myself convinced
> that "blocks" might be a good idea. For bragging rights, if nothing
> else .


Ruby's blocks are part of a larger feature that most Ruby
afficianados think is well worth the price of admission.

The typical use of a block in Ruby is to inject some code
into a predefined routine. The code will be executed with
a yield statement. In general, that block has access to the
routine's variables, and vice versa. The routine is normally
some kind of loop over a collection. The block itself is
placed after the calling parameter list.

After a lot of pondering, I think that it wouldn't be that hard
to add anonomous functions to Python, but without the other
features it would be somewhat less than overwhelmingly
useful, as it is in Ruby.

A lot of people have tried to get a nice syntax over the years,
and have failed to find anything persuasive. So I conclude that
there isn't a "nice" syntax that will get universal acclaim.
Therefore, the following will work, and might even be
relatively easy to implement.

foobar = {def (...):
suite
}

The trick is that on encountering the opening brace the current
indentation environment is suspended (stacked) and a new
indentation environment is created. The left boundary of the
new environment is the first non white-space character on the
next non-comment line. The new indentation environment is
released when the closing brace is encountered, and the prior
one is reinstated.

I'd like this form of function definition for interactive fiction (IF)
games, since it allows me to create a function and put it into
an instance with one operation, rather than having to create
the function at the module level, and then bind it into the instance
with another function call after the definition is complete.

John Roth


 
Reply With Quote
 
Hung Jung Lu
Guest
Posts: n/a
 
      04-07-2004
Ville Vainio <(E-Mail Removed)> wrote in message news:<(E-Mail Removed)>...
> I participated in a short thread on c.l.ruby, and had myself convinced
> that "blocks" might be a good idea. For bragging rights, if nothing
> else .
> ...
> foreach([(1,2), (2,4), (3,6)]) block(x,y):
> assert y == X*2


Ruby's block is quite a bizarre animal. It's amazing how people can
make a big mess out of something so simple and so fundamental. A more
useful concept is plain-vanilla codeblock. I have mentioned before
syntax like:

def my_codeblock:
assert(y == x*2)

for (x,y) in [(1,2), (2,4), (3,6)]:
exec my_codeblock

Which you actually can do in Python, if you replace the def statement
with:

my_codeblock = compile('assert(y == x*2)', '<string>', 'exec')

------------------

Python's lack of compile-time, easily-debuggable codeblock is one of
the obvious major shortcomings/mistakes of the language. Codeblocks
are absolutely wonderful for metaprogramming, to the point of being
essential. This topic has been discussed before, and I am not going
back there. When people are short-sighted, there is no cure. Other
people can take over the issue.

regards,

Hung Jung
 
Reply With Quote
 
Jeff Epler
Guest
Posts: n/a
 
      04-08-2004
>>>def naked(f):
.... return f.func_code
....
>>> def my_codeblock():

.... assert 2*x == y
....
>>> my_codeblock = naked(my_codeblock)
>>> for (x,y) in [(1,2), (2,4), (3,6)]:

.... exec my_codeblock
....
>>> for (x,y) in [(3,4)]:

.... exec my_codeblock
....
Traceback (most recent call last):
File "<stdin>", line 2, in ?
File "<stdin>", line 2, in my_codeblock
AssertionError

If the function decorator PEP gets adopted, something like
def my_codeblock() [naked]: ...
will replace 'def my_codeblock ... my_codeblock = naked(my_codeblock)'.

However, the 'exec <code object>' approach has its limits. This won't
work:
def g():
caller = sys._getframe(1).f_locals
exec my_codeblock in caller

def f():
x, y = 2, 4
g()
even if you do something fancy to get ahold of the caller's locals,
because changes to locals are either undefined or forbidden, I don't
recall which.

I'm sure Ruby's blocks are great, *in ruby*. But grafting blocks onto
Python would be like grafting an elephant's trunk onto a snake: the
elephant is cool, its trunk is cool, but a snake with a trunk is not as
great as it sounds. Instead of adding a trunk so the snake can pick
things up, just have the snake wrap its body around the object in
question.

If some Ruby code would execute its block N times, then the natural
Python translation of that code is a generator function, and the block is
the body of a for loop.

Python doesn't yet have a natural translation for code where the block is
called exactly once (resource allocation, locking, etc) but try/finally
comes close without too much pain.

Jeff

 
Reply With Quote
 
Ville Vainio
Guest
Posts: n/a
 
      04-08-2004
>>>>> "Jeff" == Jeff Epler <(E-Mail Removed)> writes:

Jeff> I'm not convinced this will fly. First, of your examples,
Jeff> two are better expressed as loops or listcomps:

True. I was merely using the examples as a way to illustrate the
syntax and semantics w/o lengthy prose.

Jeff> Every block that is executed N times should be expressed
Jeff> today as a "for" loop or listcomp with the appropriate
Jeff> generator.

Obviously. 'for' is much cleaner than Ruby blocks IMO, and generators
likewise.

>> transaction(dbconn) block(t): # commit/rollback t based on
>> exception status t.dostuff()


Jeff> I would swear there was a pep for a "with" statement, taking
Jeff> an object with special methods called at the top and the
Jeff> bottom of the statement. I can't find it, though. It would
Jeff> look something like this:

I remember such a thing too. I think it had something like 'enter' and
'leave' methods in the specified object.

Jeff> Eery block that is executed exactly 1 time should be expressed today as
Jeff> t.acquire()
Jeff> try:
Jeff> block
....

Yes, today. Ruby people are arguing that using the block to do the
trick hides the complex mechanics to the function, so the user doesn't
need to know it, therefore making Ruby 'higher level' than Python. So
this is about bragging rights .

Also, Ruby people seem to be so enthusiastic about their blocks, there
has to be something there...

Jeff> One way would be to make compound_stmt include a block_stmt alternative
Jeff> compound_stmt: block_stmt | if_stmt | ...
Jeff> block_stmt: test 'block' ['(' arglist ')'] ':' suite
Jeff> can you tell me if this is the way you imagined defining the new rule,
Jeff> and show me that this does not create a conflicting or ambiguous
Jeff> grammar? (the fact that Python prints "XXX ambiguity!" over and over

Pretty much. I really can't tell what is causing the ambiguity, you
seem to be in a much better position to do that . I merely assumed
having 'block' as a reserved word would remove any ambiguities...

Perhaps something like

'block' callable ['(' arglist ')']: suite

Then?

i.e.

block with_open_file (f):
f.write("moi")

Jeff> Finally, what do "break" and "continue" do in blocks?

The same thing they do in a function suite:

SyntaxError: 'break' outside loop

I see the blocks as analogous to functions, with the exception that
they don't introduce new scope, i.e. they could rebind names in
enclosing scope. This is a (mild) restriction in the current closure
behaviour, and blocks could be a win in this regard.

--
Ville Vainio http://tinyurl.com/2prnb
 
Reply With Quote
 
Ville Vainio
Guest
Posts: n/a
 
      04-08-2004
>>>>> "John" == John Roth <(E-Mail Removed)> writes:

John> The typical use of a block in Ruby is to inject some code
John> into a predefined routine. The code will be executed with
John> a yield statement. In general, that block has access to the

Our approach would be more elegant in the sense that block is a normal
object that is just called... yield statement seems to be redundant to
me.

John> After a lot of pondering, I think that it wouldn't be that hard
John> to add anonomous functions to Python, but without the other

Yes, apparently Guido just time-machined it in as lambda .

John> features it would be somewhat less than overwhelmingly
John> useful, as it is in Ruby.

I could tolerate a less than overwhelmingly useful feature, if it
wasn't too intrusive. Blocks wouldn't break much (well, they would add
a new grammar production) but they would give some of the stuff that
we get some flak over from Ruby & Lisp people (with-open-file) in a
rather general manner (i.e. more general than acquire/release special
syntax).

John> I'd like this form of function definition for interactive fiction (IF)
John> games, since it allows me to create a function and put it into
John> an instance with one operation, rather than having to create
John> the function at the module level, and then bind it into the instance
John> with another function call after the definition is complete.

I didn't really understand that one. What's wrong with plain old def
inside the function?

I used what I called big-L notation in the c.l.ruby thread to get
'anonymous function':

def L(x,y):
return x+y


If you always use L, and refer to it in the next line, you could as
well call it anonymous .

The issue is the rules for the free variables, i.e. you can't rebind
them. Perhaps with a form of

def f():
a = 5
def g():
local(f) a
a = 20 # rebind a in f()


syntax would render blocks unnecessary...


--
Ville Vainio http://tinyurl.com/2prnb
 
Reply With Quote
 
John Roth
Guest
Posts: n/a
 
      04-08-2004

"Ville Vainio" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed)...
> >>>>> "John" == John Roth <(E-Mail Removed)> writes:

>
> John> The typical use of a block in Ruby is to inject some code
> John> into a predefined routine. The code will be executed with
> John> a yield statement. In general, that block has access to the
>
> Our approach would be more elegant in the sense that block is a normal
> object that is just called... yield statement seems to be redundant to
> me.


The trouble is that all four or five things I mentioned seem to be
requirements to get what Ruby gets out of it. Just blocks by
themselves won't do it (IMO). (And I didn't even mention that Ruby's
habit of returning the result of the last expression when you don't
have an explicit return also figures in there.
>
> John> After a lot of pondering, I think that it wouldn't be that hard
> John> to add anonomous functions to Python, but without the other
>
> Yes, apparently Guido just time-machined it in as lambda .


Actually, lambda is on Guido's regrets list, and there's a lot of
effort (some of it IMO misguided) going into trying to eliminate it.

Let's take a simple example from "Programming Ruby" (Thomas
and Hunt).

[1, 3, 5].each { | i | puts i }

I could do the same thing in Python with:

for i in [1, 3, 5]:
print i

Just adding a block feature wouldn't let me approximate the
Ruby one-liner. I need some form of iterator to drive the block.
Python's for statement is a perfectly adequate procedureal
mechanism; Python does not have the OO (that is, Smalltalkish)
equivalent. Maybe it should, but my point here is that you need
more than just blocks to get close to what Ruby does with
blocks.

> John> features it would be somewhat less than overwhelmingly
> John> useful, as it is in Ruby.


> I could tolerate a less than overwhelmingly useful feature, if it
> wasn't too intrusive. Blocks wouldn't break much (well, they would add
> a new grammar production) but they would give some of the stuff that
> we get some flak over from Ruby & Lisp people (with-open-file) in a
> rather general manner (i.e. more general than acquire/release special
> syntax).
>
> John> I'd like this form of function definition for interactive

fiction (IF)
> John> games, since it allows me to create a function and put it into
> John> an instance with one operation, rather than having to create
> John> the function at the module level, and then bind it into the

instance
> John> with another function call after the definition is complete.
>
> I didn't really understand that one. What's wrong with plain old def
> inside the function?


What function? One of the basic characteristics of interactive fiction
as a domain is that most (90% +) of the objects are singletons. That is
to say, you need *one* instance of an object with unique behavior (not
just unique data). There's no way of easily adding a method to an
instance, only to a class.

Prototype based languages are a much better match to this particular
application domain. It might be possible to add a similar facility
to Python with a custom __getattribute__() method, but that still
doesn't give an easy way of defining methods in instances.

John Roth

>
>
> --
> Ville Vainio http://tinyurl.com/2prnb



 
Reply With Quote
 
Jeff Epler
Guest
Posts: n/a
 
      04-08-2004
> Jeff> Finally, what do "break" and "continue" do in blocks?
>

On Thu, Apr 08, 2004 at 08:50:42AM +0300, Ville Vainio wrote:
> The same thing they do in a function suite:
>
> SyntaxError: 'break' outside loop
>
> I see the blocks as analogous to functions, with the exception that
> they don't introduce new scope, i.e. they could rebind names in
> enclosing scope. This is a (mild) restriction in the current closure
> behaviour, and blocks could be a win in this regard.


Is this all the better Ruby does with it? I'd expect the ability to
make 'break' and 'continue' work "normally", when the block is used in a
looplike way.

If we do blocks, I'd expect some wording to do each of the following:
block seq.each() (i):
if i % 2: continue
print i
.... prints the even numbers in the range
def firstprime(seq):
block seq.each() (i):
if isprime(i): return i
.... returns the value returned in the block, or None otherwise
def allprime(seq):
block seq.each() (i):
if not isprime(i):
break
else:
return True
return False
.... uses break and else as in for loops.

If blocks-executed-N-times are going to be less powerful than 'for i in
iterable', then there's even less reason for them.

Jeff

 
Reply With Quote
 
Ville Vainio
Guest
Posts: n/a
 
      04-08-2004
>>>>> "John" == John Roth <(E-Mail Removed)> writes:

John> The trouble is that all four or five things I mentioned seem
John> to be requirements to get what Ruby gets out of it. Just
John> blocks by

Perhaps, but we already have most of what Ruby gets for it.

John> Let's take a simple example from "Programming Ruby" (Thomas
John> and Hunt).

John> [1, 3, 5].each { | i | puts i }

John> I could do the same thing in Python with:

John> for i in [1, 3, 5]:
John> print i

John> Just adding a block feature wouldn't let me approximate the
John> Ruby one-liner. I need some form of iterator to drive the
John> block.

But creating the iterator is trivial:

class iterblock:
def __init__(self, iter):
self.iter = iter
def __call__(self, block):
for it in self.iter:
bloc.call(it)

Usage:

l = [1,2,3]

block iterblock(l) (item):
print item

Yes, I know, this syntax doesn't look too clear when the iterator is
created with args. Python list could grow each, but that would suck
because other iterables wouldn't work. And python 'for' loop iteration
is better anyway, so it's just academic.

John> Python's for statement is a perfectly adequate procedureal
John> mechanism; Python does not have the OO (that is,
John> Smalltalkish) equivalent. Maybe it should, but my point here

I don't see a reason why an OO equivalent would be better.

John> just unique data). There's no way of easily adding a method to an
John> instance, only to a class.

But if the method is in the instance, wouldn't it be enough to have as
an attribute the curried version of a function that takes self as the
first argument? The first arg would be curried to be the object into
which the method is injected...

With the (hopefully, knock wood) upcoming decorator syntax:

o = getActor()

def sayname(self, name) [inject_to(o)]:
return self.name + " is my name"

Where inject_to(obj) injects the version of function that always
passes obj as the first parameter to o.

--
Ville Vainio http://tinyurl.com/2prnb
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Methods and blocks - not that clear when blocks passed into Steven Taylor Ruby 9 04-27-2009 08:46 AM
Coexistency Problem: cygwin + Ruby for Window (and suggestion for improving the Ruby installer) Ronald Fischer Ruby 6 05-21-2007 08:32 AM
"Building Blocks" are "Application Blocks" Arjen ASP .Net 3 02-27-2005 01:06 AM
procs/blocks - blocks with procs, blocks with blocks? matt Ruby 1 08-06-2004 01:33 AM



Advertisments