Re: [Python-Dev] map, filter, reduce, lambda

On Thursday, Jan 2, 2003, at 22:09 US/Eastern, python-dev-request@python.org wrote:
For Py3K, I might suggest "anon" instead of lambda, especially if the construct were expanded to allow statements.
If I weren't reading this list via digests on a 9,600 bps link that is only up three times a day, I would have beat Barry to that suggestion about 10 messages ago... ;-) +1 When I originally had lambda calculus thoroughly pounded into my head w/the anonymous clue-by-four, it didn't really click until someone said 'lambda functions are just anonymous functions'. Right. So why the heck are they called 'lambda'? Merely to make the less clueful CS students feel even more lost than they otherwise might? 'anon' sounds like a great name -- unlikely to be used, shorter than 'lambda', and a heck of lot more indicative as to what is going on. I'd just as soon live without the parens and *I* came from a Lisp/Scheme environment when I learned All About the Wonderful World of Lambda. b.bum

On Friday, Jan 3, 2003, at 00:42 US/Eastern, Gisle Aas wrote:
Either makes more sense than 'lambda'. I prefer 'anon' because it is a very common abbreviation for 'anonymous' and because it would have reduced the scarring during the learning of lambda calculus in CS so many years ago. 'fn' and 'sub' don't seem to be much differentiated from 'def'. 'fun' would be better than 'fn', though' because that's what lambda functions are... b.bum

On Fri, Jan 03, 2003 at 09:05:02AM -0500, Bill Bumgarner wrote:
Of course, you could just extend the syntax of 'def'. the 'funcdef' statement remains as now: funcdef: 'def' NAME parameters ':' suite suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT but the 'anon_funcdef' expression would be something like anon_funcdef: 'def' parameters ':' suite No new keyword needs to be introduced, and the fact that they have the same name is an even bigger encouragement to identify funcdef and anon_funcdef as working in the same way. (I don't think any of these problems are related specifically to the use of 'def' for both anonymous and named functions, but I'll use my own suggested spelling throughout the following) The first kind of problem with this is figuring out how to make the parser recognize set_callback(def (): print "I am a callback, short and stout" print "Here is my handle, here is my spout", ()) correctly (that comma could either end the first argument to set_callback(), or it could mean to not print a newline after the second print statement. Then the empty tuple could be a (rather silly) third statement of the anon_funcdef, or a second parameter to set_callback(). Lua handles this by having another token to end a function definition. This is probably not going to happen in Python. Instead, you have to find a way to recognize it based on its indentation. So the above example would presumably be interpreted as a 1-argument call to set_callback(), and you'd have to write something like set_callback(def (): print "I am a callback, short and stout" print "Here is my handle, here is my spout" , ()) to be parsed as a 2-argument call to set_callback. But there are two problems with this. First, when inside grouping characters ((), [], {}), the production of INDENT and DEDENT tokens is suppressed. So you'll need to find a way to turn it back on inside the anon_funcdef production. This is a kind of coupling that I don't think yet exists between the tokenizer and parser. (Right now, 'level' is incremented when '(', '[', or '{' is seen, and decremented when ')', ']' or '}' is seen, and INDENT/DEDENT processing is suppressed whenever 'level' is nonzero) Second, you must correctly maintain the indentation level stack in a somewhat new way. In set_callback(def (): INDENT print "I am a callback, short and stout" the INDENT is not from the level of set_callback to the level of 'print', it's from some intermediate and unspecified level to the level of the print token. So you must add machinery to specify that there's some anonymous indentation level. Then, on the DEDENT in print "Here is my handle, here is my spout" DEDENT , ()) you must produce the dedent token and pop that one level of unspecified indent. I'm not sure if this is enough to handle nested anonymous functions. For instance, are there cases of "ambiguous indentation", similer to the following, that would be interpreted wrongly with the suggested method I gave above? def(): def(): pass return 1 You must also decide whether you want to accept f(def(): pass) in which case the ) must generate a DEDENT for the corresponding anonymous INDENT. On the other hand, maybe you feel comfortable requiring f(def(): pass ) The second kind of problem is that people want to be able to write the moral equivalent of code like def f(): i=0 with_lock o.l: if o.m(): i=1 but due to the way nested scopes work, you can't write def f(): i=0 with_lock(o.l, def(): if o.m(): i=1) just like you can't write def f(): i=0 def g(): i=1 g() return i and have the value of i in the scope of f be modified by the assignment. So the rules of nested scopes would require changes to make anonymous functions useful. It seems that this also complicates some simpler uses of lambda. For instance, right now f(lambda: 1, lambda: 0) works, but f(def(): return 1, def(): return 0) doesn't. The body of the first anon_funcdef is 'return 1, def (): return 0'. You have to write f((def(): return 1), def(): return 0) or, for the sake of symmetry f((def(): return 1), (def(): return 0)) that's nearly a 50% increase in the number of characters. I suppose that the production for anon_funcdef could be changed compared to somewhat alleviate this: anon_funcdef: 'def' [parameters] ':' ( test | complex_stmt ) complex_stmt: NEWLINE INDENT stmt+ DEDENT (also let an empty parameter list be omitted) then I think you could still write f(def: 1, def: 0) since the 'test' form of anon_funcdef can be recognized when the token '1' is hit (instead of NEWLINE), and the ',' is recognized as being after the 'test' token. And, due to the shorter spelling of 'def' than 'lambda', you've even gained a few characters. But this means that a 1-liner anon_funcdef has a different syntax than a 1-liner funcdef, which is a mark against this idea. Well, sorry that this message got a bit long, but I think there are some significant issues to resolve before this (cool, imo) feature can be added to the language. Unfortunately, the difficulty implementing some of them is likely to outweigh the cool, certainly in the eyes of the people with the power to decide whether to include the feature. Jeff

On Fri, Jan 03, 2003 at 11:03:22AM -0600, Jeff Epler wrote:
Okay, apparently I'm wrong about there being no ambiguity in the grammar. See another subthread where Guido points this out. At least, I'm assuming he's right here. But if we decide that we want anonymous functions that work like expressions, mightn't we decide we want anonymous classes that work like expressions? For instance, instead of def run_server(server_class, socket_class, *args): class Server(server_logic, socket_class): pass Server(*args).run() run_server(DNSServer, UDPSocket) you would write def run_server(server_class, socket_class, *args): (class (server_logic, socket_class): pass)(*args).run() Obviously I've crossed over into the land of the insane here, but I am failing to have the insight to understand why (one-liner) anonymous functions are so useful to many Python programmers, but why a one-liner anonymous class seems like such a frankenstein monster. Of course, right now you could write def run_server(server_class, socket_class, *args): new.classobj("<Server>", (server_class, socket_class), {})(*args).run() but I suspect my first alternative is still preferred. Not being a java user I get the impression that little anonymous classes (or at least classes whose name is not important) are sometimes declared but that this is due to some kind of language wart more than a real desire to do this. Is this executive summary accurate? ("inner classes" or somesuch name?) Jeff

On Friday, Jan 3, 2003, at 00:42 US/Eastern, Gisle Aas wrote:
Either makes more sense than 'lambda'. I prefer 'anon' because it is a very common abbreviation for 'anonymous' and because it would have reduced the scarring during the learning of lambda calculus in CS so many years ago. 'fn' and 'sub' don't seem to be much differentiated from 'def'. 'fun' would be better than 'fn', though' because that's what lambda functions are... b.bum

On Fri, Jan 03, 2003 at 09:05:02AM -0500, Bill Bumgarner wrote:
Of course, you could just extend the syntax of 'def'. the 'funcdef' statement remains as now: funcdef: 'def' NAME parameters ':' suite suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT but the 'anon_funcdef' expression would be something like anon_funcdef: 'def' parameters ':' suite No new keyword needs to be introduced, and the fact that they have the same name is an even bigger encouragement to identify funcdef and anon_funcdef as working in the same way. (I don't think any of these problems are related specifically to the use of 'def' for both anonymous and named functions, but I'll use my own suggested spelling throughout the following) The first kind of problem with this is figuring out how to make the parser recognize set_callback(def (): print "I am a callback, short and stout" print "Here is my handle, here is my spout", ()) correctly (that comma could either end the first argument to set_callback(), or it could mean to not print a newline after the second print statement. Then the empty tuple could be a (rather silly) third statement of the anon_funcdef, or a second parameter to set_callback(). Lua handles this by having another token to end a function definition. This is probably not going to happen in Python. Instead, you have to find a way to recognize it based on its indentation. So the above example would presumably be interpreted as a 1-argument call to set_callback(), and you'd have to write something like set_callback(def (): print "I am a callback, short and stout" print "Here is my handle, here is my spout" , ()) to be parsed as a 2-argument call to set_callback. But there are two problems with this. First, when inside grouping characters ((), [], {}), the production of INDENT and DEDENT tokens is suppressed. So you'll need to find a way to turn it back on inside the anon_funcdef production. This is a kind of coupling that I don't think yet exists between the tokenizer and parser. (Right now, 'level' is incremented when '(', '[', or '{' is seen, and decremented when ')', ']' or '}' is seen, and INDENT/DEDENT processing is suppressed whenever 'level' is nonzero) Second, you must correctly maintain the indentation level stack in a somewhat new way. In set_callback(def (): INDENT print "I am a callback, short and stout" the INDENT is not from the level of set_callback to the level of 'print', it's from some intermediate and unspecified level to the level of the print token. So you must add machinery to specify that there's some anonymous indentation level. Then, on the DEDENT in print "Here is my handle, here is my spout" DEDENT , ()) you must produce the dedent token and pop that one level of unspecified indent. I'm not sure if this is enough to handle nested anonymous functions. For instance, are there cases of "ambiguous indentation", similer to the following, that would be interpreted wrongly with the suggested method I gave above? def(): def(): pass return 1 You must also decide whether you want to accept f(def(): pass) in which case the ) must generate a DEDENT for the corresponding anonymous INDENT. On the other hand, maybe you feel comfortable requiring f(def(): pass ) The second kind of problem is that people want to be able to write the moral equivalent of code like def f(): i=0 with_lock o.l: if o.m(): i=1 but due to the way nested scopes work, you can't write def f(): i=0 with_lock(o.l, def(): if o.m(): i=1) just like you can't write def f(): i=0 def g(): i=1 g() return i and have the value of i in the scope of f be modified by the assignment. So the rules of nested scopes would require changes to make anonymous functions useful. It seems that this also complicates some simpler uses of lambda. For instance, right now f(lambda: 1, lambda: 0) works, but f(def(): return 1, def(): return 0) doesn't. The body of the first anon_funcdef is 'return 1, def (): return 0'. You have to write f((def(): return 1), def(): return 0) or, for the sake of symmetry f((def(): return 1), (def(): return 0)) that's nearly a 50% increase in the number of characters. I suppose that the production for anon_funcdef could be changed compared to somewhat alleviate this: anon_funcdef: 'def' [parameters] ':' ( test | complex_stmt ) complex_stmt: NEWLINE INDENT stmt+ DEDENT (also let an empty parameter list be omitted) then I think you could still write f(def: 1, def: 0) since the 'test' form of anon_funcdef can be recognized when the token '1' is hit (instead of NEWLINE), and the ',' is recognized as being after the 'test' token. And, due to the shorter spelling of 'def' than 'lambda', you've even gained a few characters. But this means that a 1-liner anon_funcdef has a different syntax than a 1-liner funcdef, which is a mark against this idea. Well, sorry that this message got a bit long, but I think there are some significant issues to resolve before this (cool, imo) feature can be added to the language. Unfortunately, the difficulty implementing some of them is likely to outweigh the cool, certainly in the eyes of the people with the power to decide whether to include the feature. Jeff

On Fri, Jan 03, 2003 at 11:03:22AM -0600, Jeff Epler wrote:
Okay, apparently I'm wrong about there being no ambiguity in the grammar. See another subthread where Guido points this out. At least, I'm assuming he's right here. But if we decide that we want anonymous functions that work like expressions, mightn't we decide we want anonymous classes that work like expressions? For instance, instead of def run_server(server_class, socket_class, *args): class Server(server_logic, socket_class): pass Server(*args).run() run_server(DNSServer, UDPSocket) you would write def run_server(server_class, socket_class, *args): (class (server_logic, socket_class): pass)(*args).run() Obviously I've crossed over into the land of the insane here, but I am failing to have the insight to understand why (one-liner) anonymous functions are so useful to many Python programmers, but why a one-liner anonymous class seems like such a frankenstein monster. Of course, right now you could write def run_server(server_class, socket_class, *args): new.classobj("<Server>", (server_class, socket_class), {})(*args).run() but I suspect my first alternative is still preferred. Not being a java user I get the impression that little anonymous classes (or at least classes whose name is not important) are sometimes declared but that this is due to some kind of language wart more than a real desire to do this. Is this executive summary accurate? ("inner classes" or somesuch name?) Jeff
participants (3)
-
Bill Bumgarner
-
Gisle Aas
-
Jeff Epler