Re: [Python-ideas] Ruby-style Blocks in Python Idea

While there are several complaining that all this can be done with a def, there's an critical distinction being overlooked. Passing around code blocks is a very different style of programming that Python or most languages like it have ever experimented with. The programming art itself hasn't really even explored the different range of thinking this style of programming opens up. Where as most function definitions are verb-like, this would be a noun-like definition. Akin perhaps to the difference between a hormone in the body and a neurotransmitter, respectively.Setting it off with a new keyword, or expanding the use of lambda is really a mandatory way of signifying this INTENT. marcos

On Mon, Mar 9, 2009 at 9:43 AM, average <dreamingforward@gmail.com> wrote:
Your claim that this is somehow something new seems to be overlooking Lisp and Smalltalk, as well as Ruby which was mentioned in the quoted blog post. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

Acknowledged. However, the power of such a language as Lisp is under-appreciated (as I think all who know it can agree), and the problem with it (and the challenge of language design in general) is how best to *orgranize* that power; i.e. that generality. In my view, Lisp is like assembly language for the mind. It's powerful, but not easily organized or visualized into a fashion where one can see and evaluate the building up of and into higher-level constructs. MY point was really about how the programming art *itself* hasn't fully explored this concept to even be able to *evaluate* the power and usefulness of employing techniques such as code blocks. To me, Python and Ruby are both exciting and interesting examples of how language design is evolving to find ways to express and evolve that power. In my mind, there is no doubt that languages will have to find elegant ways to express that power. What's cool about Python and Ruby is that it's taking that vast general space of the "mind's assembly language" and distilling it down into nicely manageble and elegant chunks of language syntax. The concept of distinct, passable code blocks is a nice example of that compression, one that certainly has correspondence within our biology. Thanks for the dialog though... marcos

average wrote:
On the contrary, I think Smalltalk has explored it very well. Smalltalk implements *all* control structures in terms of code blocks, and does so in a very readable way, without any of the brain-exploding characteristics of Lisp. It works well in Smalltalk because the whole language syntax is designed from the ground up to accommodate it. Ruby inherits the idea, but struggles to fit it into its syntax, leading to a much-weakened form (you can only pass one code block to a given method at a time). It's even harder to fit the idea into Python's syntax. This isn't just because of the indentation issue, but also because Pythonistas tend to have a higher standard of aesthetics when comes to syntax design. Ruby can get away with looking a bit messy and haphazard, but that's not acceptable in the Python community. There are also semantic problems with the idea in Python. Once you're allowed to write the code block in-line, it becomes expected that you can write things like: while some_condition: with flapple() do (arg): if some_other_condition: break and have the 'break' exit from the while-loop. But if the body is actually a separate function, this is not easy to arrange. Back when the existing with-statement was being designed, there was serious thought put towards implementing it by passing the body as a function. But handling 'break', 'continue', 'return' and 'yield' inside the body would have required raising special control-flow exceptions, and it all got very messy and complicated. In the end it was decided not to be worth the hassle, and the existing generator-based implementation was settled on. -- Greg

Hey Greg,
Thanks for this!! It's the only counter-argument that I've seen which demonstrates that my proposal was unpythonic. As such, I'd like to withdraw my proposal -- sorry for taking up everybody's time =( But, hey, live and learn =) -- love, tav plex:espians/tav | tav@espians.com | +44 (0) 7809 569 369 http://tav.espians.com | http://twitter.com/tav | skype:tavespian

average wrote:
[RE:"using" keyword].Setting it off with a new keyword, or
A function is a possibly parameterized code block that can be passed around and called. Anonymity is a defect, not an advantage. So your attempted differentiation looks a bit like mystical gibberish to me. Sorry.

On Tue, 10 Mar 2009 03:43:03 am average wrote:
Marcos, I'm afraid that I don't understand your analogy, or your argument. An anonymous code block is just like a named function, except it doesn't have a name. Can you explain why: func(named_function) is radically different from: func(multi-line-code-block-without-the-name) please? We can already do this in Python, using functions made up of a single expression: func(lambda: expr) I use this frequently, because it is sometimes convenient, but there is nothing I can do with a lambda that I can't do with a named function. I don't see that introducing multi-lined lambdas will change that. I'm trying to keep an open-mind here, but I also don't understand your analogy. In what way are named functions like neurotransmitters, or hormones? Which one is supposed to be verb-like and which one is noun-like? What does that even mean? -- Steven D'Aprano

Hey Steven,
Hmz, the intention isn't to support multi-line lambdas. It's to make passing in anonymous functions easier. For precedence let's take a look at decorators. Fundamentally, decorators save a user nothing more than a single line of code. Why do @foo, when you could just do: func = foo(func) ? But saving developers that extra line of typing has obviously been useful -- you can find decorators used pretty heavily in many of the major Python frameworks nowadays... By easing up some of the hassle, we can encourage certain forms of development. -- love, tav plex:espians/tav | tav@espians.com | +44 (0) 7809 569 369 http://tav.espians.com | http://twitter.com/tav | skype:tavespian

On Mon, Mar 9, 2009 at 10:15 AM, tav <tav@espians.com> wrote:
Well, that works only as long as there is only a single anonymous function to pass in and it's the last argument. Plus it doesn't work with an existing function that takes a function argument -- your function (if I understand your proposed __do__ implemtation correctly) must really be a generator.
For precedence let's take a look at decorators. Fundamentally, decorators save a user nothing more than a single line of code.
I guess you weren't there at the time. If it was about saving a line of code it would have been boohed out of the room. (Especially since the line count is actually the same with or without using the decorator syntax!) The big improvement that decorators offer is to move the "decoration" from the end of the function body, where it is easily missed, to the front of the declaration, where it changes the emphasis for the reader. I don't see a similar advantage in your example; it looks more like "Ruby-envy" to me. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On Tue, 10 Mar 2009 04:15:13 am you wrote:
Lambdas are single-line (technically, single-statement) anonymous functions, and it's already easy to pass them in: caller(lambda args: statement) A multi-line lambda (technically, multi-statement) would also be an anonymous function. Your syntax: using caller (args) do: multiple lines making an anonymous function seems to me to be defining a multi-line lambda. The three lines of the indented block are exactly equivalent to the statement of a lambda. What is the difference that you see? -- Steven D'Aprano

Steven D'Aprano schrieb:
If you really want to get technical, it's single-expression. nit-pickingly-yrs, Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.

Steven D'Aprano wrote:
Lambdas are single-line (technically, single-statement) anonymous functions,
Lambdas are function-defining expressions used *within* statements that give the resulting function object a stock .__name__ of '<lambda>'. The syntax could have been augmented to include a real name, so the stock-name anonymity is a side-effect of the chosen syntax. Possibilities include lambda name(args): expression lambda <name> args: expression The latter, assuming it is LL(1) parse-able, would even be compatible with existing code and could still be added. Contrarywise, function-defining def statements could have been allowed to omit the name. To be useful, the object (with a .__name__ such as '<def>', would have to get a default namespace binding such as to '_', even in batch mode.
caller(lambda args: statement)
Change 'statement' to 'expression'.
A multi-line lambda (technically, multi-statement)
The problem is that 'multi-statement expression' is an oxymoron in Pythonland.
would also be an anonymous function.
Not necessarily, and irrelevant to the essence of lambda expressions, which is that they are expressions that can be used within statements. Terry Jan Reedy

Le Mon, 09 Mar 2009 18:04:32 -0400, Terry Reedy <tjreedy@udel.edu> s'exprima ainsi:
I do not agree with that. It is missing the point of lambdas. Lambdas are snippets of code equivalent to expressions to be used in place. Lambdas are *not* called, need not beeing callable, rather they are *used* by higher order functions like map. The fact that they do not have any name in syntax thus properly matches their semantic "anonymousity" ;-)
I do not see any contradiction with the "essence of lambda expressions" here. We could have a syntax for multi-statement lambdas without any semantic contradiction. The issue is more probably that it does not fit well python's style and syntax (esp. indent) and would hinder legibility and simplicity. print map(lambda x: {fact = None if x<0 else factorial(x); return "%s: %s" %(x,fact)}, seq) It's ugly, sure. Still, I do not see the "essence of lambda expressions" twisted. I vote -1 to "block-lambdas" for the sake of clarity only. Denis ------ la vita e estrany

On Tue, Mar 10, 2009 at 1:19 AM, spir <denis.spir@free.fr> wrote:
I do not agree with that. It is missing the point of lambdas. Lambdas are snippets of code equivalent to expressions to be used in place. Lambdas are *not* called, need not beeing callable, rather they are *used* by higher order functions like map. The fact that they do not have any name in syntax thus properly matches their semantic "anonymousity" ;-)
Eh? On what planet do you live? What use is a lambda if it is never called? It will *eventually* be called -- if it is never called you might as well substitute None and your program would run the same. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

Le Tue, 10 Mar 2009 10:05:30 -0700, Guido van Rossum <guido@python.org> s'exprima ainsi:
Should have written: "called in code" (!). Thought it was obvious, sorry. It's executed indeed through the func it is passed to, but not (explicitely) called. The point I meant is: it is locally used -- not called from anywhere else -- the reason why it needs no name. denis ------ la vita e estrany

On Tue, Mar 10, 2009 at 11:38 AM, spir <denis.spir@free.fr> wrote:
OK, I understand what you're saying now. But I don't agree with your position that if it isn't used locally it doesn't deserve a name. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

spir wrote:
??? A lambda expression and a def statement both produce function objects. The *only* difference between the objects produced by a lambda expression and the equivalent def statement is that the former gets the stock .__name__ of '<lambda>, which is less useful that a specific name should there be a traceback. The important difference between a function-defining expression and statement is that the former can be used in expression context within statements and the latter cannot. tjr

Le Mon, 9 Mar 2009 09:43:03 -0700, average <dreamingforward@gmail.com> s'exprima ainsi:
This style of programming is very common in stack-based languages, or rather in concatenative languages in general. :square dup * # def square(x): return x*x :squares [square] map # def squares(l): return [square(x) for x in l] # using equivalent of first class func :squares [dup *] map # using anonymous func def [1 2 3] squares ==> [1 4 9] In the latter form, the func literal expression -- often called 'quotation' because it 'quotes' code without executing it -- can be whatever and as long as needed, which is easy due to the linear style of stack-based programming. "Higher order" functions like map, called combinators, are very frequent and (unlike in other paradigms) make the code clearer. But they are not really higher order functions, rather they take several kinds of data as input, one of which happens to be code that will be 'unquoted', i.e. run -- closer to Lisp's eval. see http://www.latrobe.edu.au/philosophy/phimvt/joy/faq.html for a nice introduction (but rather distinctive to FP) esp section #10 denis ------ la vita e estrany

On Mon, Mar 9, 2009 at 9:43 AM, average <dreamingforward@gmail.com> wrote:
Your claim that this is somehow something new seems to be overlooking Lisp and Smalltalk, as well as Ruby which was mentioned in the quoted blog post. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

Acknowledged. However, the power of such a language as Lisp is under-appreciated (as I think all who know it can agree), and the problem with it (and the challenge of language design in general) is how best to *orgranize* that power; i.e. that generality. In my view, Lisp is like assembly language for the mind. It's powerful, but not easily organized or visualized into a fashion where one can see and evaluate the building up of and into higher-level constructs. MY point was really about how the programming art *itself* hasn't fully explored this concept to even be able to *evaluate* the power and usefulness of employing techniques such as code blocks. To me, Python and Ruby are both exciting and interesting examples of how language design is evolving to find ways to express and evolve that power. In my mind, there is no doubt that languages will have to find elegant ways to express that power. What's cool about Python and Ruby is that it's taking that vast general space of the "mind's assembly language" and distilling it down into nicely manageble and elegant chunks of language syntax. The concept of distinct, passable code blocks is a nice example of that compression, one that certainly has correspondence within our biology. Thanks for the dialog though... marcos

average wrote:
On the contrary, I think Smalltalk has explored it very well. Smalltalk implements *all* control structures in terms of code blocks, and does so in a very readable way, without any of the brain-exploding characteristics of Lisp. It works well in Smalltalk because the whole language syntax is designed from the ground up to accommodate it. Ruby inherits the idea, but struggles to fit it into its syntax, leading to a much-weakened form (you can only pass one code block to a given method at a time). It's even harder to fit the idea into Python's syntax. This isn't just because of the indentation issue, but also because Pythonistas tend to have a higher standard of aesthetics when comes to syntax design. Ruby can get away with looking a bit messy and haphazard, but that's not acceptable in the Python community. There are also semantic problems with the idea in Python. Once you're allowed to write the code block in-line, it becomes expected that you can write things like: while some_condition: with flapple() do (arg): if some_other_condition: break and have the 'break' exit from the while-loop. But if the body is actually a separate function, this is not easy to arrange. Back when the existing with-statement was being designed, there was serious thought put towards implementing it by passing the body as a function. But handling 'break', 'continue', 'return' and 'yield' inside the body would have required raising special control-flow exceptions, and it all got very messy and complicated. In the end it was decided not to be worth the hassle, and the existing generator-based implementation was settled on. -- Greg

Hey Greg,
Thanks for this!! It's the only counter-argument that I've seen which demonstrates that my proposal was unpythonic. As such, I'd like to withdraw my proposal -- sorry for taking up everybody's time =( But, hey, live and learn =) -- love, tav plex:espians/tav | tav@espians.com | +44 (0) 7809 569 369 http://tav.espians.com | http://twitter.com/tav | skype:tavespian

average wrote:
[RE:"using" keyword].Setting it off with a new keyword, or
A function is a possibly parameterized code block that can be passed around and called. Anonymity is a defect, not an advantage. So your attempted differentiation looks a bit like mystical gibberish to me. Sorry.

On Tue, 10 Mar 2009 03:43:03 am average wrote:
Marcos, I'm afraid that I don't understand your analogy, or your argument. An anonymous code block is just like a named function, except it doesn't have a name. Can you explain why: func(named_function) is radically different from: func(multi-line-code-block-without-the-name) please? We can already do this in Python, using functions made up of a single expression: func(lambda: expr) I use this frequently, because it is sometimes convenient, but there is nothing I can do with a lambda that I can't do with a named function. I don't see that introducing multi-lined lambdas will change that. I'm trying to keep an open-mind here, but I also don't understand your analogy. In what way are named functions like neurotransmitters, or hormones? Which one is supposed to be verb-like and which one is noun-like? What does that even mean? -- Steven D'Aprano

Hey Steven,
Hmz, the intention isn't to support multi-line lambdas. It's to make passing in anonymous functions easier. For precedence let's take a look at decorators. Fundamentally, decorators save a user nothing more than a single line of code. Why do @foo, when you could just do: func = foo(func) ? But saving developers that extra line of typing has obviously been useful -- you can find decorators used pretty heavily in many of the major Python frameworks nowadays... By easing up some of the hassle, we can encourage certain forms of development. -- love, tav plex:espians/tav | tav@espians.com | +44 (0) 7809 569 369 http://tav.espians.com | http://twitter.com/tav | skype:tavespian

On Mon, Mar 9, 2009 at 10:15 AM, tav <tav@espians.com> wrote:
Well, that works only as long as there is only a single anonymous function to pass in and it's the last argument. Plus it doesn't work with an existing function that takes a function argument -- your function (if I understand your proposed __do__ implemtation correctly) must really be a generator.
For precedence let's take a look at decorators. Fundamentally, decorators save a user nothing more than a single line of code.
I guess you weren't there at the time. If it was about saving a line of code it would have been boohed out of the room. (Especially since the line count is actually the same with or without using the decorator syntax!) The big improvement that decorators offer is to move the "decoration" from the end of the function body, where it is easily missed, to the front of the declaration, where it changes the emphasis for the reader. I don't see a similar advantage in your example; it looks more like "Ruby-envy" to me. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On Tue, 10 Mar 2009 04:15:13 am you wrote:
Lambdas are single-line (technically, single-statement) anonymous functions, and it's already easy to pass them in: caller(lambda args: statement) A multi-line lambda (technically, multi-statement) would also be an anonymous function. Your syntax: using caller (args) do: multiple lines making an anonymous function seems to me to be defining a multi-line lambda. The three lines of the indented block are exactly equivalent to the statement of a lambda. What is the difference that you see? -- Steven D'Aprano

Steven D'Aprano schrieb:
If you really want to get technical, it's single-expression. nit-pickingly-yrs, Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.

Steven D'Aprano wrote:
Lambdas are single-line (technically, single-statement) anonymous functions,
Lambdas are function-defining expressions used *within* statements that give the resulting function object a stock .__name__ of '<lambda>'. The syntax could have been augmented to include a real name, so the stock-name anonymity is a side-effect of the chosen syntax. Possibilities include lambda name(args): expression lambda <name> args: expression The latter, assuming it is LL(1) parse-able, would even be compatible with existing code and could still be added. Contrarywise, function-defining def statements could have been allowed to omit the name. To be useful, the object (with a .__name__ such as '<def>', would have to get a default namespace binding such as to '_', even in batch mode.
caller(lambda args: statement)
Change 'statement' to 'expression'.
A multi-line lambda (technically, multi-statement)
The problem is that 'multi-statement expression' is an oxymoron in Pythonland.
would also be an anonymous function.
Not necessarily, and irrelevant to the essence of lambda expressions, which is that they are expressions that can be used within statements. Terry Jan Reedy

Le Mon, 09 Mar 2009 18:04:32 -0400, Terry Reedy <tjreedy@udel.edu> s'exprima ainsi:
I do not agree with that. It is missing the point of lambdas. Lambdas are snippets of code equivalent to expressions to be used in place. Lambdas are *not* called, need not beeing callable, rather they are *used* by higher order functions like map. The fact that they do not have any name in syntax thus properly matches their semantic "anonymousity" ;-)
I do not see any contradiction with the "essence of lambda expressions" here. We could have a syntax for multi-statement lambdas without any semantic contradiction. The issue is more probably that it does not fit well python's style and syntax (esp. indent) and would hinder legibility and simplicity. print map(lambda x: {fact = None if x<0 else factorial(x); return "%s: %s" %(x,fact)}, seq) It's ugly, sure. Still, I do not see the "essence of lambda expressions" twisted. I vote -1 to "block-lambdas" for the sake of clarity only. Denis ------ la vita e estrany

On Tue, Mar 10, 2009 at 1:19 AM, spir <denis.spir@free.fr> wrote:
I do not agree with that. It is missing the point of lambdas. Lambdas are snippets of code equivalent to expressions to be used in place. Lambdas are *not* called, need not beeing callable, rather they are *used* by higher order functions like map. The fact that they do not have any name in syntax thus properly matches their semantic "anonymousity" ;-)
Eh? On what planet do you live? What use is a lambda if it is never called? It will *eventually* be called -- if it is never called you might as well substitute None and your program would run the same. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

Le Tue, 10 Mar 2009 10:05:30 -0700, Guido van Rossum <guido@python.org> s'exprima ainsi:
Should have written: "called in code" (!). Thought it was obvious, sorry. It's executed indeed through the func it is passed to, but not (explicitely) called. The point I meant is: it is locally used -- not called from anywhere else -- the reason why it needs no name. denis ------ la vita e estrany

On Tue, Mar 10, 2009 at 11:38 AM, spir <denis.spir@free.fr> wrote:
OK, I understand what you're saying now. But I don't agree with your position that if it isn't used locally it doesn't deserve a name. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

spir wrote:
??? A lambda expression and a def statement both produce function objects. The *only* difference between the objects produced by a lambda expression and the equivalent def statement is that the former gets the stock .__name__ of '<lambda>, which is less useful that a specific name should there be a traceback. The important difference between a function-defining expression and statement is that the former can be used in expression context within statements and the latter cannot. tjr

Le Mon, 9 Mar 2009 09:43:03 -0700, average <dreamingforward@gmail.com> s'exprima ainsi:
This style of programming is very common in stack-based languages, or rather in concatenative languages in general. :square dup * # def square(x): return x*x :squares [square] map # def squares(l): return [square(x) for x in l] # using equivalent of first class func :squares [dup *] map # using anonymous func def [1 2 3] squares ==> [1 4 9] In the latter form, the func literal expression -- often called 'quotation' because it 'quotes' code without executing it -- can be whatever and as long as needed, which is easy due to the linear style of stack-based programming. "Higher order" functions like map, called combinators, are very frequent and (unlike in other paradigms) make the code clearer. But they are not really higher order functions, rather they take several kinds of data as input, one of which happens to be code that will be 'unquoted', i.e. run -- closer to Lisp's eval. see http://www.latrobe.edu.au/philosophy/phimvt/joy/faq.html for a nice introduction (but rather distinctive to FP) esp section #10 denis ------ la vita e estrany
participants (8)
-
average
-
Georg Brandl
-
Greg Ewing
-
Guido van Rossum
-
spir
-
Steven D'Aprano
-
tav
-
Terry Reedy