A friendlier, sugarier lambda -- a proposal for Ruby-like blocks in python
Hi all -- Compared to the Python I know and love, Ruby isn't quite the same. However, it has at least one terrific feature: "blocks". Whereas in Python a "block" is just several lines of locally-scoped-together code, in Ruby a "block" defines a closure (anonymous function). To avoid confusion let's call them Ruby block-closures. I see them as just a syntax for defining closures and passing them into method calls. I think something analogous could be added to Python in a very simple manner that would make closures much more readable and usable, and nail some use cases nicely. To define a new closure and pass it into a function call, there are two current methods: inline 'def' and 'lambda'. Consider the following Twisted-ish code: deferred = fetchPage('http://python.org') def _showResponse(response) print "fancy formatting: %s" % response.text deferred.addCallback(_showResponse) Lots of Twisted code has to be written backwards like this. Theoretically, it might be nice to use lambda right in the addCallback() call, like: deferred.addCallback(lambda r: print("fancy formatting %s" %r.text) ) But this is awkward since the lambda is constrained to be one line; you can't come back later and add much to the callback's code. Furthermore, this example isn't even legal, because 'print' isn't a function, but a statement -- lambda is further constrained to only contain an expression. Many have complained about this crippled-ness of lambda, but it actually makes some sense. Since Python uses colons and indentation to define blocks of code, it would be awkward to close a multiline lambda. The best I could think of would look like deferred.addCallback(lambda r: print("fancy formatting %s" % r.text) ) ^ | That trailing paranthesis is WAY un-Pythonic. We don't close code blocks like that! And in general, declaring big multiline anonymous functions in the middle of a list of normal variable arguments is weird -- it just doesn't fit. It's perfectly legal to pass in 4 closures, interspersed with number and string arguments. Imagine defining all of those inline with 'lambda' expressions! And what about nesting? And then there's the term "lambda", while a great homage to Lisp and computation theory, just isn't the friendliest programming vocab term. (from my limited understanding,) Ruby block-closures assume a specific use case: You want to pass exactly one multiline, defined-right-there closure to a method when calling it. Therefore, you should get to define the closure *immediately following* the method call. I suggest a Python version with a keyword 'using' (or 'with'?) that takes the block of code as a closure, and passes it to the method call as the *last argument*. The above example becomes: deferred.addCallback() using response: print "fancy formatting %s" % response.text and in general, the following two code snippets are equivalent: def _f(x,y): [do stuff with x and y] function_with_callback(a,b,c, _f) function_with_callback(a,b,c) using x,y: [do stuff with x and y] next_statement() ... where function_with_callback wants a 2-arg function as its last argument. It gets to call _f, or equivalently the defined-right-there closure/ruby-block, however it wants to -- wait for an I/O operation to finish, whatever. I'm not so hot about the fact that it looks like addCallback() should be completed before the 'using' keyword kicks in, but this is the simplest I could think of. This syntax does not let you define a new function and store it as a local variable. Python already has inline 'def' for that (that is, you can do a 'def' in any block of code you want, and it stays local to that scope.) It does not, strictly speaking, let you do anything new -- as Guido has stated, you could ditch lambda and achieve the equivalent by declaring the little callback function as an inline 'def', like in the first deferred example here. This only optimizes for the case of defining a closure only for the purpose of passing it in as an argument to a method. However, this must be the only use for anonymous functions in Python, since we already have inline 'def'. I've always felt that passing in a lambda in lisp and python, or the equivalent anonymous function(x,y) {} in javascript, or anonymous classes in java, was always awkward. You have to think about defining a new anonymous function *within* your method call and such, and then you have to close parantheses after it -- it's just weird, I dunno. This proposal could also handle some of the use cases mentioned in PEP 310 and 340. If you design your callback-taking functions to have only one callback and have it as the last argument, you can trivially write lock acquition (omit 'using' for a no-arg block-closure): def protect(lock, f): lock.acquire() f() lock.release() protect(myLock): [do stuff that needs myLock to be acquire
Re: A friendlier, sugarier lambda -- a proposal for Ruby-like blocks in python
[repost -- fixed formatting] Hi all -- Compared to the Python I know and love, Ruby isn't quite the same. However, it has at least one terrific feature: "blocks". Whereas in Python a "block" is just several lines of locally-scoped-together code, in Ruby a "block" defines a closure (anonymous function). To avoid confusion let's call them Ruby block-closures. I see them as just a syntax for defining closures and passing them into method calls. I think something analogous could be added to Python in a very simple manner that would make closures much more readable and usable, and nail some use cases nicely. To define a new closure and pass it into a function call, there are two current methods: inline 'def' and 'lambda'. Consider the following Twisted-ish code: deferred = fetchPage('http://python.org') def _showResponse(response) print "fancy formatting: %s" % response.text deferred.addCallback(_showResponse) Lots of Twisted code has to be written backwards like this. Theoretically, it might be nice to use lambda right in the addCallback() call, like: deferred.addCallback(lambda r: print("fancy formatting %s" %r.text) ) But this is awkward since the lambda is constrained to be one line; you can't come back later and add much to the callback's code. Furthermore, this example isn't even legal, because 'print' isn't a function, but a statement -- lambda is further constrained to only contain an expression. Many have complained about this crippled-ness of lambda, but it actually makes some sense. Since Python uses colons and indentation to define blocks of code, it would be awkward to close a multiline lambda. The best I could think of would look like deferred.addCallback(lambda r: print("fancy formatting %s" % r.text) ) ^ | That trailing paranthesis is WAY un-Pythonic. We don't close code blocks like that! And in general, declaring big multiline anonymous functions in the middle of a list of normal variable arguments is weird -- it just doesn't fit. It's perfectly legal to pass in 4 closures, interspersed with number and string arguments. Imagine defining all of those inline with 'lambda' expressions! And what about nesting? And then there's the term "lambda", while a great homage to Lisp and computation theory, just isn't the friendliest programming vocab term. (from my limited understanding,) Ruby block-closures assume a specific use case: You want to pass exactly one multiline, defined-right-there closure to a method when calling it. Therefore, you should get to define the closure *immediately following* the method call. I suggest a Python version with a keyword 'using' (or 'with'?) that takes the block of code as a closure, and passes it to the method call as the *last argument*. The above example becomes: deferred.addCallback() using response: print "fancy formatting %s" % response.text and in general, the following two code snippets are equivalent: def _f(x,y): [do stuff with x and y] function_with_callback(a,b,c, _f) function_with_callback(a,b,c) using x,y: [do stuff with x and y] next_statement() ... where function_with_callback wants a 2-arg function as its last argument. It gets to call _f, or equivalently the defined-right-there closure/ruby-block, however it wants to -- wait for an I/O operation to finish, whatever. I'm not so hot about the fact that it looks like addCallback() should be completed before the 'using' keyword kicks in, but this is the simplest I could think of. This syntax does not let you define a new function and store it as a local variable. Python already has inline 'def' for that (that is, you can do a 'def' in any block of code you want, and it stays local to that scope.) It does not, strictly speaking, let you do anything new -- as Guido has stated, you could ditch lambda and achieve the equivalent by declaring the little callback function as an inline 'def', like in the first deferred example here. This only optimizes for the case of defining a closure only for the purpose of passing it in as an argument to a method. However, this must be the only use for anonymous functions in Python, since we already have inline 'def'. I've always felt that passing in a lambda in lisp and python, or the equivalent anonymous function(x,y) {} in javascript, or anonymous classes in java, was always awkward. You have to think about defining a new anonymous function *within* your method call and such, and then you have to close parantheses after it -- it's just weird, I dunno. This proposal could also handle some of the use cases mentioned in PEP 310 and 340. If you design your callback-taking functions to have only one callback and have it as the last argument, you can trivially write lock acquition (omit 'using' for a no-arg block-closure): def protect(lock, f): lock.acquire() f() lock.release() protect(myLock): [do stuff th
Re: A friendlier, sugarier lambda -- a proposal for Ruby-like blocks in python
Kay Schluehr wrote: > The with statement is already implemented in Python 2.5. > > http://docs.python.org/whatsnew/pep-343.html > > The main difference between the with statement and Ruby blocks is that > the with-statement does not support loops. Yielding a value of a > function decorated with a contextmanager and passing it to the BLOCK of > the with statement is essentially a one-shot. Therefore you can't use > the with statement to define iterators. It is not a lightweight visitor > pattern replacement as it is in Ruby. Hence the with- and the > for-statement are orthogonal to each other in Python. Thanks or the What's-New link, it clarified things for me. So there are several ways to do things with code blocks now in python.. * for/while define loops around their blocks * if defines contional control into its block * with defines startup/cleanup context surrounding its block Twisted addCallback() is a different pattern than either of these. The code is deferred to execute at some later time. If there are many more patterns of things you could want to do with a block, it might be nice to have a blocks-are-closures mechanism. -- http://mail.python.org/mailman/listinfo/python-list