Ruby blocks in Python, a suggestion

Discussion in 'Python' started by Ville Vainio, Apr 7, 2004.

  1. Ville Vainio

    Ville Vainio Guest

    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
     
    Ville Vainio, Apr 7, 2004
    #1
    1. Advertising

  2. Ville Vainio

    Jeff Epler Guest

    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
     
    Jeff Epler, Apr 7, 2004
    #2
    1. Advertising

  3. Ville Vainio

    John Roth Guest

    "Ville Vainio" <> wrote in message
    news:...
    > 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
     
    John Roth, Apr 7, 2004
    #3
  4. Ville Vainio

    Hung Jung Lu Guest

    Ville Vainio <> wrote in message news:<>...
    > 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
     
    Hung Jung Lu, Apr 7, 2004
    #4
  5. Ville Vainio

    Jeff Epler Guest

    >>>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
     
    Jeff Epler, Apr 8, 2004
    #5
  6. Ville Vainio

    Ville Vainio Guest

    >>>>> "Jeff" == Jeff Epler <> 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
     
    Ville Vainio, Apr 8, 2004
    #6
  7. Ville Vainio

    Ville Vainio Guest

    >>>>> "John" == John Roth <> 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
     
    Ville Vainio, Apr 8, 2004
    #7
  8. Ville Vainio

    John Roth Guest

    "Ville Vainio" <> wrote in message
    news:...
    > >>>>> "John" == John Roth <> 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
     
    John Roth, Apr 8, 2004
    #8
  9. Ville Vainio

    Jeff Epler Guest

    > 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
     
    Jeff Epler, Apr 8, 2004
    #9
  10. Ville Vainio

    Ville Vainio Guest

    >>>>> "John" == John Roth <> 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
     
    Ville Vainio, Apr 8, 2004
    #10
  11. Ville Vainio

    Jim Jewett Guest

    "John Roth" <> wrote in message news:<>...

    > 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.


    What do you mean by inject? Are you saying that you can
    inherit from any code object, and insert into the middle?

    For instance could I do something like this:

    class A(object):
    def m1(self):
    func1()
    func2()

    class B(object):
    def m1(self):
    func1()
    func1a()
    func2()

    without having to retype func1 and func2, or even having to worry
    about them, so that class B will be robust against changes to class A?

    If so, how does func1a find it's place? pattern matching?

    If not, what is the point? Just a function which *can* modify its
    enclosing scope? (or a macro, depending on your perspective)? Or
    am I still missing something?

    -jJ
     
    Jim Jewett, Apr 9, 2004
    #11
  12. Ville Vainio

    John Roth Guest

    "Jim Jewett" <> wrote in message
    news:...
    > "John Roth" <> wrote in message

    news:<>...
    >
    > > 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.

    >
    > What do you mean by inject? Are you saying that you can
    > inherit from any code object, and insert into the middle?


    "The code will be executed with a yield statement."

    I guess that wasn't clear. In order to execute a block,
    the function has to have a yield statement. That yield
    statement is what calls the block, and passes it any
    parameters it requires. (It's quite different from a Python
    yield statement.) That's part of Ruby's "one block per function"
    semantics. (The other part is that the block is placed after
    the function's parameter list in a syntactically distinct place.
    It's not a parameter in the normal sense.)

    The reason for saying "inject" is that, under some
    circumstances, the block can access the host function's
    locals, which you wouldn't expect from a normal function
    that's passed as a parameter.

    John Roth
     
    John Roth, Apr 9, 2004
    #12
  13. On Wed, 7 Apr 2004 14:55:13 -0400, "John Roth"
    <> wrote:
    [snip]
    >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
    > }
    >


    This to me looks not much better than:

    def f(...):
    suite
    foobar = f

    I recently put together a page for the UserLinux project, attempting
    to show any fundamental advantages of Ruby over Python.
    http://userlinux.com/cgi-bin/wiki.pl?RubyPython

    After much discussion, I concluded that 90% of the differences are
    just personal preference, and the other 10% are so complex that an
    ordinary Python programmer can't understand :>). I did find that
    string methods were a bit easier in Ruby ( see the page above ).

    Code blocks were a topic we never resolved. There are some examples
    on the page above, and on a page with a link you will see at the
    bottom of that page. If you can come up with a good example,
    something that has a little more detail that your example above, but
    not so much that we can't follow it, I will post that example on our
    page.

    -- Dave
     
    David MacQuigg, Apr 10, 2004
    #13
  14. While I like the idea of blocks in Python, that's very bad syntax for
    it. I think something better would be

    def run_with_args(**args, &block):
    block(**args)
    run_with_args(something, somethingelse) as this, that:
    assert something == this
    assert somethingelse == that

    This is contrived, but I think this shows the basic syntax. Also, you
    miss the point of why we need blocks: they allow for more abstraction,
    especially syntactic abstraction. This is important, but it seems to
    have missed the designers of Python. Part of the Python philosophy is
    that if some syntax was needed, it should already be in the language.
    Although that works most of the time, it fails for things like GUIs.
    It seems like every GUI library has its own idea of how to respond to
    events. Almost always, they rely on named functions sent as arguments
    for this, but the functions are only used once. To me, it seems better
    to do what Ruby and Smalltalk do: use anonymous blocks. These make for
    cleaner and more consistent code.

    Daniel Ehrenberg

    Ville Vainio <> wrote in message news:<>...
    > 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 :).
     
    Daniel Ehrenberg, Apr 11, 2004
    #14
  15. Ville Vainio

    John Roth Guest

    "Daniel Ehrenberg" <> wrote in message
    news:...
    > While I like the idea of blocks in Python, that's very bad syntax for
    > it. I think something better would be
    >
    > def run_with_args(**args, &block):
    > block(**args)
    > run_with_args(something, somethingelse) as this, that:
    > assert something == this
    > assert somethingelse == that
    >
    > This is contrived, but I think this shows the basic syntax. Also, you
    > miss the point of why we need blocks: they allow for more abstraction,
    > especially syntactic abstraction. This is important, but it seems to
    > have missed the designers of Python. Part of the Python philosophy is
    > that if some syntax was needed, it should already be in the language.
    > Although that works most of the time, it fails for things like GUIs.
    > It seems like every GUI library has its own idea of how to respond to
    > events. Almost always, they rely on named functions sent as arguments
    > for this, but the functions are only used once. To me, it seems better
    > to do what Ruby and Smalltalk do: use anonymous blocks. These make for
    > cleaner and more consistent code.


    As it turns out, at least for the work I'm doing with Tkinter, Python
    supports callbacks without any problem at all. The difficulty is that
    the standard references don't describe what you need to do very
    well.

    What's the magic solution? Use bound methods. If you're doing
    anything substantial with an interface you've encapsulated it in a
    class anyway. Using an anonymous function is simply adding an
    unnecessary level of indirection. That's only necessary if you're
    using a procedural programming style.

    Also, I think you may have missed two of my major points.

    1. I like the notion of blocks myself. I'm certainly not arguing
    against them, and if you think I am, you've misinterprered what
    I've been saying.

    2. The utility of blocks in Ruby is due to the intersection of
    several different features. Just adding blocks to Python isn't
    going to suddenly enable what you can do in Ruby with them.

    John Roth
     
    John Roth, Apr 11, 2004
    #15
  16. In article <>, John Roth wrote:
    > As it turns out, at least for the work I'm doing with Tkinter, Python
    > supports callbacks without any problem at all. The difficulty is that
    > the standard references don't describe what you need to do very
    > well.
    >
    > What's the magic solution? Use bound methods. If you're doing
    > anything substantial with an interface you've encapsulated it in a
    > class anyway. Using an anonymous function is simply adding an
    > unnecessary level of indirection. That's only necessary if you're
    > using a procedural programming style.


    From my experience, this is very accurate. A bound method is usually a very
    convenient way to set up a callback, since it carries the state of the
    object you are wiring the GUI component to. In addition, it has by far the
    cleanest syntax that Python has to offer for this situation.

    I can think of a few situations where you might need an event callback that
    does not wire the GUI component to an object, however. For instance, say you
    had a text field and you wanted to make sure it contained no spaces. In this
    case, it's arguably not a concern of any other object. Ie.:

    def removeAllSpaces(tf):
    tf.text = tf.text.replace(' ', '')

    tf = TextField()
    tf.onChange = removeAllSpaces

    Now, with code blocks, it could be written this way:

    tf = TextField()
    tf.onChange = [ tf | tf.text = tf.text.replace(' ', '') ]

    With a bound method, it would be:

    tf.onChange = someOtherObject.removeAllSpaces

    But is this really the concern of any other object than the TextField? It
    might be, depending on the situation, but in many cases there is no object
    that is clearly responsible for this type of behavior besides the TextField
    itself. So, to be OO about it, I suppose you could inherit from TextField
    (if the API allows you to do so) and create a SpaceRemovingTextField, but
    this seems like unnecessary namespace clutter as well as a useless
    abstraction (unless you need this feature in many places).

    > 2. The utility of blocks in Ruby is due to the intersection of
    > several different features. Just adding blocks to Python isn't
    > going to suddenly enable what you can do in Ruby with them.


    Excellent point.

    --
    ..:[ dave benjamin: ramen/[sp00] -:- spoomusic.com -:- ramenfest.com ]:.
    : please talk to your son or daughter about parametric polymorphism. :
     
    Dave Benjamin, Apr 12, 2004
    #16
  17. Ville Vainio

    John Roth Guest

    "Dave Benjamin" <> wrote in message
    news:...
    > In article <>, John Roth wrote:
    > > As it turns out, at least for the work I'm doing with Tkinter, Python
    > > supports callbacks without any problem at all. The difficulty is that
    > > the standard references don't describe what you need to do very
    > > well.
    > >
    > > What's the magic solution? Use bound methods. If you're doing
    > > anything substantial with an interface you've encapsulated it in a
    > > class anyway. Using an anonymous function is simply adding an
    > > unnecessary level of indirection. That's only necessary if you're
    > > using a procedural programming style.

    >
    > From my experience, this is very accurate. A bound method is usually a

    very
    > convenient way to set up a callback, since it carries the state of the
    > object you are wiring the GUI component to. In addition, it has by far the
    > cleanest syntax that Python has to offer for this situation.
    >
    > I can think of a few situations where you might need an event callback

    that
    > does not wire the GUI component to an object, however. For instance, say

    you
    > had a text field and you wanted to make sure it contained no spaces. In

    this
    > case, it's arguably not a concern of any other object. Ie.:
    >
    > def removeAllSpaces(tf):
    > tf.text = tf.text.replace(' ', '')
    >
    > tf = TextField()
    > tf.onChange = removeAllSpaces
    >
    > Now, with code blocks, it could be written this way:
    >
    > tf = TextField()
    > tf.onChange = [ tf | tf.text = tf.text.replace(' ', '') ]
    >
    > With a bound method, it would be:
    >
    > tf.onChange = someOtherObject.removeAllSpaces
    >
    > But is this really the concern of any other object than the TextField? It
    > might be, depending on the situation, but in many cases there is no object
    > that is clearly responsible for this type of behavior besides the

    TextField
    > itself. So, to be OO about it, I suppose you could inherit from TextField
    > (if the API allows you to do so) and create a SpaceRemovingTextField, but
    > this seems like unnecessary namespace clutter as well as a useless
    > abstraction (unless you need this feature in many places).


    That's a good point, but it only applies if you haven't
    encapsulated the rest of the interaction with the TextField
    widgit. If you have, making it a bound method of that
    object is real cheap, and it's rather obvious that the field
    edits are a responsibility of the encapsulating object.

    John Roth

    > --
    > .:[ dave benjamin: ramen/[sp00] -:- spoomusic.com -:- ramenfest.com ]:.
    > : please talk to your son or daughter about parametric polymorphism. :
     
    John Roth, Apr 12, 2004
    #17
  18. Ville Vainio

    Ville Vainio Guest

    >>>>> "John" == John Roth <> writes:

    John> The reason for saying "inject" is that, under some
    John> circumstances, the block can access the host function's
    John> locals, which you wouldn't expect from a normal function
    John> that's passed as a parameter.

    I think that's what they call 'dynamic scope', and it has been
    generally considered a bad idea.

    --
    Ville Vainio http://tinyurl.com/2prnb
     
    Ville Vainio, Apr 13, 2004
    #18
  19. Ville Vainio

    Ville Vainio Guest

    >>>>> "Daniel" == Daniel Ehrenberg <> writes:

    Daniel> for this, but the functions are only used once. To me, it seems better
    Daniel> to do what Ruby and Smalltalk do: use anonymous blocks. These make for
    Daniel> cleaner and more consistent code.

    But if you call the function something like B, it's 'almost'
    anonymous. Actual anonymity doesn't buy you much, esp. if you can
    reuse the same name over and over.

    And speaking of syntax, perhaps something like this would do:

    def func():
    y = 12
    defblock B(x,y):
    print x+y
    y = 4


    print y # prints 4

    do_stuff(B)

    This way, the only difference b/w block and function would be the fact
    that block has the same locals() (and free vars) as the enclosing
    function.

    --
    Ville Vainio http://tinyurl.com/2prnb
     
    Ville Vainio, Apr 13, 2004
    #19
  20. Ville Vainio

    Ville Vainio Guest

    >>>>> "John" == John Roth <> writes:

    John> 2. The utility of blocks in Ruby is due to the intersection of
    John> several different features. Just adding blocks to Python isn't
    John> going to suddenly enable what you can do in Ruby with them.

    Not suddenly, but give it a couple of months. Adding various wrappers
    that use blocks instead of funcs is trivial, anyone can introduce the
    wrappers in ther own code.

    --
    Ville Vainio http://tinyurl.com/2prnb
     
    Ville Vainio, Apr 13, 2004
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Arjen
    Replies:
    3
    Views:
    471
    Scott Allen
    Feb 27, 2005
  2. Replies:
    26
    Views:
    715
    Paul Boddie
    Oct 17, 2006
  3. matt
    Replies:
    1
    Views:
    296
    George Ogata
    Aug 6, 2004
  4. Ronald Fischer
    Replies:
    6
    Views:
    345
    Ronald Fischer
    May 21, 2007
  5. Steven Taylor
    Replies:
    9
    Views:
    279
    Brian Candler
    Apr 27, 2009
Loading...

Share This Page