There was an extensive discussion about this in the past. https://www.artima.com/weblogs/viewpost.jsp?thread=147358 In the end Guido felt that his effort seems to him like building a Rube Goldberg machine. By looking at the amount of people still stumbling on this issue we definitely feel, that it is not the. Such a machine is a useful one. "Naming things" was from the beginning one the two hardest things in programming. Good naming is important for understandable code. The balance of extracted, named functions, as most of things in programming, is subjective though. The python style guide itself encourages people to use simple data transformations like `map`, `filter`, `reduce` for expressiveness. Those transformations can still be simple with 2 or 3 statements and will not require naming. Naming the transformation becomes harder, than reading the actual code. Coffeescript, which is another indent based language, handles multi statement lambdas without curly braces. *arr = []* *arr.map((x)-> * * y = x+1* * y * 2* *)* There is also an argument in this discussion, that just using curly braces for lambdas (which is easier to implement) would be pretty unpythonic. I really agree that python curly braces free block style is really helpful for readability in contrast to javascript for example. However using curly braces for lambdas in ruby is not hurting the eyes that much. Normally blocks in ruby use `do .. end` notation. Thus lambdas are clearly visible and also people try not to nest multiple levels of lamda transfromations. We strive to keep such a chain flat. *def transform arr* * .map { ... }* * .filter { ... } .reduce { ... }* In the end curly branes for lambdas is also a mathematical notation. Guido had a hard opinion on this, but i hope the python community does not. A powerful general purpose language should not limit itself to one statement in a closure. Lets add mutli-statement lambdas to python either with just curly braces or with an indent based machine.
On Mon, Oct 22, 2018 at 3:07 AM Andreas Winschu <andrej.wins@gmail.com> wrote:
Guido had a hard opinion on this, but i hope the python community does not. A powerful general purpose language should not limit itself to one statement in a closure. Lets add mutli-statement lambdas to python either with just curly braces or with an indent based machine.
Just to be clear here, a *closure* is fully capable of having multiple statements in it. You can use 'def' inside a function, and it will create a closure. Proposals like this stand or fall on the syntax to be used. Can you be completely specific about exactly what the syntax is that you're proposing, and give some examples? Don't forget that this is currently valid code: func(lambda x: {}) so you'll need to clearly distinguish your block delimiters from set/dict display. ChrisA
A powerful general purpose language should not limit itself to one statement in a closure.
Nitpick on language: It doesn't. Lambdas are not the only way to do a closure. It's important to be precise when discussing these things.
Lets add mutli-statement lambdas to python either with just curly braces or with an indent based machine.
Mostly it's just better with a function. Do you have some more compelling examples where you can't use a function smoothly? / Anders
A def function has to be named. Wheres a lambda expression can be passed anonymously to any other function as an argument. *map(lambda x: x**2, array)* vs *def powers2(x)* * x**2* *map(powers2, array)* Coming up with a name here is not needed, as the operation is expressive enough. Am So., 21. Okt. 2018 um 18:17 Uhr schrieb Anders Hovmöller < boxed@killingar.net>:
A powerful general purpose language should not limit itself to one statement in a closure.
Nitpick on language: It doesn't. Lambdas are not the only way to do a closure.
It's important to be precise when discussing these things.
Lets add mutli-statement lambdas to python either with just curly braces or with an indent based machine.
Mostly it's just better with a function. Do you have some more compelling examples where you can't use a function smoothly?
/ Anders
-- Gruß Andreas Winschu
Wheres a lambda expression can be passed anonymously to any other function as an argument.
map(lambda x: x**2, array)
vs
def powers2(x) x**2 map(powers2, array)
Coming up with a name here is not needed, as the operation is expressive enough.
Sure, but there is a well known convention for such non-names already: meta syntactical variables. In this case the name could be "foo" and all readers will interpret this as "no name". I admit to have wanted lambdas that are just as powerful as normal functions, but often when I want it the code is nicer when a normal function is used. / Anders
Multi-line lambdas may be nice for functional languages, but Python is very imperative in nature and prefers clean syntax over conciseness, which means it will make it REALLY hard to fit with Python. I personally find multi-line lambdas an unreadable abomination. As for some more concrete reasons on why this should not be a part of Python, see here for an example which shows why it would even be hard to come up with a plausible syntax for multi-line lambdas: https://stackoverflow.com/questions/1233448/no-multiline-lambda-in-python-wh... Guido's words: "But the complexity of any proposed solution for this puzzle is immense, to me: it requires the parser (or more precisely, the lexer) to be able to switch back and forth between indent-sensitive and indent-insensitive modes, keeping a stack of previous modes and indentation level. Technically that can all be solved (there's already a stack of indentation levels that could be generalized). But none of that takes away my gut feeling that it is all an elaborate Rube Goldberg contraption." If one would find a syntax which is Pythonic and clean then I am in favor of adding it, but I argue it simply does not exist. - Ron [image: Facebook] <http://www.facebook.com/ron.reiter> [image: Twitter] <http://twitter.com/#!/ronreiter> [image: LinkedIn] <https://il.linkedin.com/in/ronreiter> On Sun, Oct 21, 2018 at 7:34 PM Anders Hovmöller <boxed@killingar.net> wrote:
Wheres a lambda expression can be passed anonymously to any other function as an argument.
*map(lambda x: x**2, array)*
vs
*def powers2(x)* * x**2* *map(powers2, array)*
Coming up with a name here is not needed, as the operation is expressive enough.
Sure, but there is a well known convention for such non-names already: meta syntactical variables. In this case the name could be "foo" and all readers will interpret this as "no name".
I admit to have wanted lambdas that are just as powerful as normal functions, but often when I want it the code is nicer when a normal function is used.
/ Anders _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Hi, What about writing longer map&reduces in pyspark? When I worked with Spark and Scala, it was easy to script a longer chain of transformations in Scala. Does anybody know how that works in Python without multiline lambdas? Cheers Marko Le dim. 21 oct. 2018 à 18:40, Ron Reiter <ron.reiter@gmail.com> a écrit :
Multi-line lambdas may be nice for functional languages, but Python is very imperative in nature and prefers clean syntax over conciseness, which means it will make it REALLY hard to fit with Python. I personally find multi-line lambdas an unreadable abomination.
As for some more concrete reasons on why this should not be a part of Python, see here for an example which shows why it would even be hard to come up with a plausible syntax for multi-line lambdas:
https://stackoverflow.com/questions/1233448/no-multiline-lambda-in-python-wh...
Guido's words:
"But the complexity of any proposed solution for this puzzle is immense, to me: it requires the parser (or more precisely, the lexer) to be able to switch back and forth between indent-sensitive and indent-insensitive modes, keeping a stack of previous modes and indentation level. Technically that can all be solved (there's already a stack of indentation levels that could be generalized). But none of that takes away my gut feeling that it is all an elaborate Rube Goldberg contraption."
If one would find a syntax which is Pythonic and clean then I am in favor of adding it, but I argue it simply does not exist.
- Ron
[image: Facebook] <http://www.facebook.com/ron.reiter> [image: Twitter] <http://twitter.com/#!/ronreiter> [image: LinkedIn] <https://il.linkedin.com/in/ronreiter>
On Sun, Oct 21, 2018 at 7:34 PM Anders Hovmöller <boxed@killingar.net> wrote:
Wheres a lambda expression can be passed anonymously to any other function as an argument.
*map(lambda x: x**2, array)*
vs
*def powers2(x)* * x**2* *map(powers2, array)*
Coming up with a name here is not needed, as the operation is expressive enough.
Sure, but there is a well known convention for such non-names already: meta syntactical variables. In this case the name could be "foo" and all readers will interpret this as "no name".
I admit to have wanted lambdas that are just as powerful as normal functions, but often when I want it the code is nicer when a normal function is used.
/ Anders _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Just write all your transformations at the top of the file and then write the mapreduce chain at the end. If you need to use closures that depend on each other then it may be a bit more ugly but still feasible to implement. - Ron [image: Facebook] <http://www.facebook.com/ron.reiter> [image: Twitter] <http://twitter.com/#!/ronreiter> [image: LinkedIn] <https://il.linkedin.com/in/ronreiter> On Sun, Oct 21, 2018 at 10:20 PM Marko Ristin-Kaufmann < marko.ristin@gmail.com> wrote:
Hi, What about writing longer map&reduces in pyspark? When I worked with Spark and Scala, it was easy to script a longer chain of transformations in Scala. Does anybody know how that works in Python without multiline lambdas?
Cheers Marko
Le dim. 21 oct. 2018 à 18:40, Ron Reiter <ron.reiter@gmail.com> a écrit :
Multi-line lambdas may be nice for functional languages, but Python is very imperative in nature and prefers clean syntax over conciseness, which means it will make it REALLY hard to fit with Python. I personally find multi-line lambdas an unreadable abomination.
As for some more concrete reasons on why this should not be a part of Python, see here for an example which shows why it would even be hard to come up with a plausible syntax for multi-line lambdas:
https://stackoverflow.com/questions/1233448/no-multiline-lambda-in-python-wh...
Guido's words:
"But the complexity of any proposed solution for this puzzle is immense, to me: it requires the parser (or more precisely, the lexer) to be able to switch back and forth between indent-sensitive and indent-insensitive modes, keeping a stack of previous modes and indentation level. Technically that can all be solved (there's already a stack of indentation levels that could be generalized). But none of that takes away my gut feeling that it is all an elaborate Rube Goldberg contraption."
If one would find a syntax which is Pythonic and clean then I am in favor of adding it, but I argue it simply does not exist.
- Ron
[image: Facebook] <http://www.facebook.com/ron.reiter> [image: Twitter] <http://twitter.com/#!/ronreiter> [image: LinkedIn] <https://il.linkedin.com/in/ronreiter>
On Sun, Oct 21, 2018 at 7:34 PM Anders Hovmöller <boxed@killingar.net> wrote:
Wheres a lambda expression can be passed anonymously to any other function as an argument.
*map(lambda x: x**2, array)*
vs
*def powers2(x)* * x**2* *map(powers2, array)*
Coming up with a name here is not needed, as the operation is expressive enough.
Sure, but there is a well known convention for such non-names already: meta syntactical variables. In this case the name could be "foo" and all readers will interpret this as "no name".
I admit to have wanted lambdas that are just as powerful as normal functions, but often when I want it the code is nicer when a normal function is used.
/ Anders _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Mon, Oct 22, 2018 at 3:29 AM Andreas Winschu <andrej.wins@gmail.com> wrote:
A def function has to be named.
Wheres a lambda expression can be passed anonymously to any other function as an argument.
map(lambda x: x**2, array)
vs
def powers2(x) x**2 map(powers2, array)
Coming up with a name here is not needed, as the operation is expressive enough.
Sure, but can you give examples where you can't use lambda, but still want it to have no name? Then show what your proposed syntax is, and how it would improve the code. Side point: I hardly ever use map with lambda functions in Python. I'll use map with an existing function (eg "map(int, list_of_strings)" to intify everything), but otherwise, I'll use a comprehension. Comprehensions are also restricted to expressions only, but your proposal won't help them. ChrisA
On Sun, Oct 21, 2018 at 6:39 PM Chris Angelico <rosuav@gmail.com> wrote:
Sure, but can you give examples where you can't use lambda, but still want it to have no name?
I can try kinda answering this: It's less with everyday built-ins and the standard library (though see first example below), and more with libraries and frameworks that want callbacks (or callables in general) passed in. Sometimes I need that callable to be something very simple, but it technically can't be a lambda because it has a statement in it. Defining it as a function forces me to give it not only a name, but a place too. Defining it just before the point of use is a natural location in one sense, but it makes the visual flow of the code worse, especially if I need to make several such steps (with different almost-lambdas) in a row.
From the standard library, I can actually think of Thread:
t1 = Thread(target=(lambda: counter += 1)) t2 = Thread(target=(lambda: sum += current_quux())) Because they include statements, it needs to be: def inc_counter(): counter += 1 t1 = Thread(target=inc_counter) def update_sum(): sum += current_quux() t2 = Thread(target=update_sum)
From one popular library:
ws = websocket.WebSocketApp( url, on_open = (lambda ws: ws.send('subscribe'); conn_counter += 1), on_message = (lambda ws, msg: print(msg); msg_counter += 1)) Because they include statements, I again need to create those functions separately. Naming isn't so much a problem here, but it would turn kludgey if I needed to make several such calls in the same scope. In any case, that's a common little frustration I often put up with. I think most other languages around Python's power level don't make that a problem. To expand that argument a bit, lambdas have access to names in their immediate context that can be more of a pain with less-local functions. For a toy example: [(lambda a: print(x + a)) for x in l] When print was a statement, defining this lambda as a named function would have had to use more arguments. The richer the context, the more noise it would have added. BTW, don't treat the above `(lambda: stmt)` syntax as a _proposal_ per se, BUT: No matter what we allow inside a lambda, from the outside it's an expression. How do we make a Python expression span multiple lines? In simplified terms, we put it in parentheses. How do we separate an expression whose boundaries would be ambiguous in a given context (e.g. a tuple literal inside a parameter list)? We put it inside a pair of parentheses. Hmm. I mean, these already work: (lambda: 2 + 3) lambda: 2 + ( 3) They're multi-LINE lambdas, just not multi-statement (or even uni-statement). If Python _was_ to somehow allow statements inside lambdas at all at some point in the future (which I don't expect), I don't see that that would then need any additional special syntax for multi-line-ness or for separating them from the surrounding context.
On Sun, Oct 21, 2018 at 11:04:02PM +0200, Vladimir Filipović wrote:
Sometimes I need that callable to be something very simple, but it technically can't be a lambda because it has a statement in it. Defining it as a function forces me to give it not only a name, but a place too. Defining it just before the point of use is a natural location in one sense, but it makes the visual flow of the code worse, especially if I need to make several such steps (with different almost-lambdas) in a row.
From the standard library, I can actually think of Thread:
t1 = Thread(target=(lambda: counter += 1)) t2 = Thread(target=(lambda: sum += current_quux()))
Because they include statements, it needs to be:
def inc_counter(): counter += 1 t1 = Thread(target=inc_counter)
I don't think that's a real working example. py> counter = 0 py> def f(): ... counter += 1 ... py> f() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in f UnboundLocalError: local variable 'counter' referenced before assignment You need to declare counter and sum as global variables.
def update_sum(): sum += current_quux() t2 = Thread(target=update_sum)
From one popular library:
ws = websocket.WebSocketApp( url, on_open = (lambda ws: ws.send('subscribe'); conn_counter += 1), on_message = (lambda ws, msg: print(msg); msg_counter += 1))
Because they include statements, I again need to create those functions separately. Naming isn't so much a problem here, but it would turn kludgey if I needed to make several such calls in the same scope.
Its not the naming that would make it turn kludgy, but the use of global variables. If you had three websockets, would you want them all to share the same counter?
In any case, that's a common little frustration I often put up with. I think most other languages around Python's power level don't make that a problem.
To expand that argument a bit, lambdas have access to names in their immediate context that can be more of a pain with less-local functions. For a toy example:
[(lambda a: print(x + a)) for x in l]
You've fallen straight into the classic eager versus late binding of closures Gotcha. py> l = "spam eggs cheese aardvark".split() py> funcs = [(lambda a: print(x + a)) for x in l] py> for f in funcs: ... f("") ... aardvark aardvark aardvark aardvark If your intention was to demonstrate that multi-statement lambda would be a bug-magnet, you have done an excellent job :-) -- Steve
On Mon, Oct 22, 2018 at 12:52 AM Steven D'Aprano <steve@pearwood.info> wrote:
def inc_counter(): counter += 1
I don't think that's a real working example. ... You need to declare counter and sum as global variables.
Good catch! None of the examples were real, in the sense of being copied directly from code in actual use. They were synthesized to illustrate a point while being as brief as possible. I'm not even really advocating for multi-statement (or statement) lambdas BTW, I was just answering the question of when one might even want an anonymous function that can't be expressed using the current lambda syntax.
Its not the naming that would make it turn kludgy, but the use of global variables. If you had three websockets, would you want them all to share the same counter?
No, the naming would turn it kludgey precisely because I'd want different functions. If there's only one "on_open", it suggests a natural name for itself. If there are multiple, they can either be called "on_open1", "on_open2"... or I can keep reusing the same name in the same scope but that would make the code less clear, not more.
You've fallen straight into the classic eager versus late binding of closures Gotcha.
I did! /facepalm Thanks. I constructed a very poor example, and it's beginning to look like the particular point I was trying to illustrate with that doesn't actually stand.
If your intention was to demonstrate that multi-statement lambda would be a bug-magnet, you have done an excellent job :-)
I don't know why you'd say that. That was a zero-statement lambda :)
On Mon, Oct 22, 2018 at 8:05 AM Vladimir Filipović <hemflit@gmail.com> wrote:
From one popular library:
ws = websocket.WebSocketApp( url, on_open = (lambda ws: ws.send('subscribe'); conn_counter += 1), on_message = (lambda ws, msg: print(msg); msg_counter += 1))
Because they include statements, I again need to create those functions separately. Naming isn't so much a problem here, but it would turn kludgey if I needed to make several such calls in the same scope.
Cool! We have an example. Next step: Offer a variety of alternate syntaxes that *do* currently work, and then the proposed multi-statement lambda, and show how the current syntaxes are all ugly. One syntax that currently can be used to great effect is decorators. I don't know the specific websocket library that you're using, but it's generally not difficult to design something so that a function call can do the assignment. It'd end up looking something like: ws = websocket.WebSocketApp(url) @ws.event def on_open(sock): sock.send('subscribe') nonlocal conn_counter # or global, not sure conn_counter += 1 @ws.event def on_message(sock, msg): print(msg) nonlocal msg_counter msg_counter += 1 Yes, it's a lot more vertical than your version. However, adding in the nonlocal declarations definitely makes it look a LOT less lambda-friendly. This also takes a bit of extra support. But it can be third-party support if you need it to. Worst case, you can make a little helper: events = {} def event(func): events[func.__name__] = func return func @event def on_open... @event def on_message... ws = websocket.WebSocketApp(url, **events) which will work with the same API. Great for larger and more complex setups. ChrisA
Chris, I'm happy to work with you to hammer out comparisons of various solutions. But I can't take on the role of an advocate for "multi-statement lambdas". I don't even understand what precisely that covers, since we don't have uni-statement lambdas. _If that role would be needed for this discussion, the rest of what I'm about to write may be a waste of your time!_ I got involved in this thread because examples of something were asked for, and I had a couple. I could defend a claim like "some _limited set_ of statements inside lambdas would be useful and pythonic", if that's a useful conversation to have. (My view BTW is that _indented compound statements_ in lambdas would be _irreducibly_ unpythonic, and the only way that can change is if the concept of pythonicity happens to drift over time.) ------
Next step: Offer a variety of alternate syntaxes that *do* currently work, and then the proposed multi-statement lambda, and show how the current syntaxes are all ugly.
Good, but we may have a disconnect about which "ugliness"/problem is being solved. The central point of Python's lambdas, for me, is that when one needs to refer to a _very simple_, _once-off_ function, lambdas _make the code better._ The ways they make it better go beyond saving vertical space and can be subtle: they improve the locality of code by allowing the function's definition to be exactly in the place where it's used; a lot like the difference between (in pseudocode) `output("Hello, world")` and having to say `String hw = new String("Hello, world"); output(hw)`. So, what does the existing lambda feature offer in those situations? 1. Virtually no pomp and ceremony (one keyword, sometimes needs parentheses, no compulsory newlines). 2. Relieves me from having to decide on the place for the definition (right before use? at the beginning of the using function/scope? right before the using function/scope?). This could be resolved by just having a convention, but I note there isn't an existing convention ("Schelling point") for the just-define-a-named-function approach. 3. That code-locality thing. It relieves me as reader from having to mentally connect its definition to its usage, and from having to deduce that it's not also used elsewhere. 4. Relieves me from having to come up with a one-use name. No, `foo` isn't as good. 5. (I shouldn't have to include this but) Expressions don't lose clarity by being moved into a lambda. Past the keyword itself, the contents of the lambda look exactly like (= as simple as) what I would write in non-lambda Python code. I don't need to contort them into special lambda-specific constructs. None of those things are _universally_ desirable! But the existing lambda lets me _choose_ that in some particular case (very simple, once-off function) they do make the code better, so I can get them. Except that sometimes I need a _very simple_, _once-off_ function that would do something that's a statement. It seems like a very similar situation, the concept doesn't seem antithetical to the philosophy of Python's existing lambdas, so it's a bit frustrating that I can't choose to use the same feature and reap those same benefits. Other times, I want the lambda to do a couple of things in sequence. No ifs or try-finallys, just one thing with its side-effects, followed by the other. This is already doable as evaluation of a tuple (if the things are expressions), and that's not _too_ ugly, but it would become more direct (as in quality #5 above) if it was expressible as a pair of expression-statements. ------ Okay now, what does the decorator solution do in that WebSocket situation that just defining named functions doesn't already? I suppose it helps somewhat with property #2 above (finding a place for the definition). Outside of that list, it lets us shorten the constructor call. Am I missing something? Or am I moving the goalposts - were you explicitly trying to solve something else? ------ Let me try to anticipate one more existing solution (the best I came up with): Where the statement is about updating counters or similar state variables, we can make them items in a dict (or collections.Counter) instead, and update them in a lambda via dict.update(). It's not terrible, but it means all other references to the state variable must change in a way that makes them less clear. ("Why are they using this dict at all? Ohh, because lambda.") And having to replace `c += 1` with `d.update(c = d['c'] + 1)` or `counter.update(c=1)` is still ugly.
On Tue, Oct 23, 2018 at 8:04 PM Vladimir Filipović <hemflit@gmail.com> wrote:
Chris, I'm happy to work with you to hammer out comparisons of various solutions.
But I can't take on the role of an advocate for "multi-statement lambdas". I don't even understand what precisely that covers, since we don't have uni-statement lambdas. _If that role would be needed for this discussion, the rest of what I'm about to write may be a waste of your time!_
If you were confused by my use of the generic "you", then I apologize. This wasn't aimed personally at you, but more generally at "this is what we need". And you're right that we don't have uni-statement lambdas, but I suspect people would be more confused by "suite lambdas" as an alternative to "expression lambdas", unless they happen to be familiar with the internal details of CPython's grammar :) I suppose in theory you could design a syntax for lambda functions that permits a simple_stmt, or maybe a compound_stmt, but both would be as messy as supporting a full suite (or multiple statements, in vernacular) and would be unnecessarily restrictive.
The central point of Python's lambdas, for me, is that when one needs to refer to a _very simple_, _once-off_ function, lambdas _make the code better._ The ways they make it better go beyond saving vertical space and can be subtle: they improve the locality of code by allowing the function's definition to be exactly in the place where it's used; a lot like the difference between (in pseudocode) `output("Hello, world")` and having to say `String hw = new String("Hello, world"); output(hw)`.
My view, which I suspect is the same as yours but written in different words, is that a lambda function should be treated as a block of code inside another function, and not as its own entity. You don't give a name to the block of code between "if" and "else", because it's just code as part of the implementation of its containing function. You don't give a name to the implicit function created by a list comprehension, because it's just ... you get the idea.
Okay now, what does the decorator solution do in that WebSocket situation that just defining named functions doesn't already?
I suppose it helps somewhat with property #2 above (finding a place for the definition). Outside of that list, it lets us shorten the constructor call.
Am I missing something? Or am I moving the goalposts - were you explicitly trying to solve something else?
Part of getting a proposal accepted is showing that there is no adequate solution using existing tools. The best way to show that is to come up with the best possible solution that we currently have, and demonstrate (if it isn't self-evident) that it's sub-par. The decorator solution is one of two that came to my mind as existing options (the other being an abuse of class syntax). Criticizing it is exactly correct, and is a way to support the assertion "we need better lambda functions".
Let me try to anticipate one more existing solution (the best I came up with):
Where the statement is about updating counters or similar state variables, we can make them items in a dict (or collections.Counter) instead, and update them in a lambda via dict.update().
It's not terrible, but it means all other references to the state variable must change in a way that makes them less clear. ("Why are they using this dict at all? Ohh, because lambda.")
And having to replace `c += 1` with `d.update(c = d['c'] + 1)` or `counter.update(c=1)` is still ugly.
Also a point worth raising. Currently, lambda functions are permitted to mutate objects in surrounding scopes, but not to rebind them. Well, actually, PEP 572 might well be the solution there, but then you reopen all that lovely controversy... I mentioned abusing class syntax as an alternative solution. This would require a new API, but it wouldn't be hard to write a wrapper. It'd end up something like this: @args(websocket.WebSocketApp, url) class ws: def on_open(...): def on_message(...): The decorator could look something like this (untested): def args(func, *a, **kw): def wrapper(cls): for name, val in cls.__dict__.items(): if not name.startswith("_"): assert name not in kw kw[name] = val return func(*a, **kw) return wrapper In some contexts, that would be every bit as good as a lambda-based solution. To compete, the proposed lambda syntax would have to be better than all these - by no means impossible, but it's a target to aim at. ChrisA
On Tue, Oct 23, 2018 at 1:16 PM Chris Angelico <rosuav@gmail.com> wrote:
a lambda function should be treated as a block of code inside another function, and not as its own entity. You don't give a name to the block of code between "if" and "else" ...
Very well put! Thanks.
Currently, lambda functions are permitted to mutate objects in surrounding scopes, but not to rebind them. Well, actually, PEP 572 might well be the solution there, but then you reopen all that lovely controversy...
I don't think I see how PEP 572 could help solve even a part of it. I mean, I get it enables expressions in general to rebind names in the surrounding scope, but it seems (I'm re-reading the scoping section right now) that lambdas are in effect excluded from that; that any `x := 1` inside a lambda necessarily refers to a local `x`.
I mentioned abusing class syntax as an alternative solution. This would require a new API, but it wouldn't be hard to write a wrapper. ... In some contexts, that would be every bit as good as a lambda-based solution. To compete, the proposed lambda syntax would have to be better than all these - by no means impossible, but it's a target to aim at.
Okay, that's... indeed looking abusive :) In situations where it's applicable, it's arguably no worse than a statement lambda for points 2-5 (placement, code locality, naming, natural code inside it). But in terms of "pomp and ceremony" (point 1) it's a bit of a step backwards compared to even writing plain named functions: it calls for multiple keywords and compulsory newlines for something very simple that shouldn't visually command that much attention. Minor point, but also understanding this construct (when reading code) needs a bit of a learning curve, and statement lambdas wouldn't. And it's only applicable with keyword arguments. I understand you expect real examples. `Thread` and `websocket.WebSocketApp` with their keyword arguments were genuine examples from my experience, whereas I couldn't say I've ever felt a need to pass a statement-lambda to `map` or `reduce`. So maybe that disqualifies the positional-arguments objection against this solution, but the verbosity complaint stands.
On Sun, Oct 21, 2018 at 06:28:40PM +0200, Andreas Winschu wrote:
A def function has to be named.
That's a feature, not a lack. As a general rule of thumb, if the body of a function is complex enough to require multiple statements, it is probably complex enough to require documentation, testing and a nice name to display in tracebacks. I wish that somebody would do an objective scientific study on the bug density of code using named functions versus anonymous code blocks. My intuition (which is, of course, completely subjective) tells me that code written in languages that allow and encourage anonymous code blocks will have more bugs, simply because coders will be less likely to name and test their functions. -- Steve
On 10/21/2018 12:28 PM, Andreas Winschu wrote
A def function has to be named.
In general, this is a good thing. It often improves tracebacks. Perhaps more importantly, name facilitate testing and mocking.
Wheres a lambda expression can be passed anonymously to any other function as an argument.
Except for relatively trivial expressions, this is a bad thing. All functions created from lambda expressions get the same pseudo-name '<lambda>'. This can make tracebacks worse. Perhaps more importantly, proper testing may become harder.
/map(lambda x: x**2, array)/
It is obvious what this trivial expression does, but without a name or comment or context indicating intent, how do I know that it is correct? Trivial examples evade the issues that arise even with multiline expressions, let alone multiple statements.
for i in map(lambda x: x ** 2, 'abc'): print(i)
Traceback (most recent call last): File "<pyshell#19>", line 2, in <module> 2, 'abc'): File "<pyshell#19>", line 2, in <lambda> 2, 'abc'): TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
vs /def powers2(x)/ / x**2/ /map(powers2, array)/
def pow2(x): return (x ** 2)
for i in map(pow2, 'abc'): print(i)
Traceback (most recent call last): File "<pyshell#26>", line 1, in <module> for i in map(pow2, 'abc'): print(i) File "<pyshell#24>", line 3, in pow2 2) TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int' Given that there might be a hundred functions named '<lambda>', I think the specific name is a bit helpful.
Coming up with a name here is not needed, as the operation is expressive enough.
It is clear as to the meaning. And testing that pow2(3) equals 9 is pretty useless. But you are proposing something else: adding anonymous multiple statement functions, where a name *and testing* is usually needed. Justification for what we have today is not justification for a major change. tkinter uses callbacks with widget commands and scheduling. idlelib has 77 matches for the re '[( ,]command='. 47 are followed by 'self.somemethod'; 26 by other function names, and 4 by lambda expressions. All four of the latter are trivial calls of a named method: self.somemethod(value). So when all named non-test functions are tested, all non-test callbacks will be tested. In spite of newbie problems with lambda, I am happy to not have to define trivial test functions like this. def undoNone(): return d.undo_event(None) But doubt that I would rather write any of the independent production-code functions and methods as anonymous lambda constructs. -- Terry Jan Reedy
On Mon, Oct 22, 2018 at 10:16 AM Terry Reedy <tjreedy@udel.edu> wrote:
On 10/21/2018 12:28 PM, Andreas Winschu wrote
A def function has to be named.
In general, this is a good thing. It often improves tracebacks. Perhaps more importantly, name facilitate testing and mocking.
Wheres a lambda expression can be passed anonymously to any other function as an argument.
Except for relatively trivial expressions, this is a bad thing. All functions created from lambda expressions get the same pseudo-name '<lambda>'. This can make tracebacks worse. Perhaps more importantly, proper testing may become harder.
The same considerations bite comprehensions, too, but we don't discourage their use. So I don't think this should be a killer - not on its own, anyhow. I do not currently support any proposed syntax for multi-statement lambda functions, mainly because they've all been ugly. But maybe there'll be one, somewhere, some day, that makes sense. ChrisA
On Mon, Oct 22, 2018 at 10:25:19AM +1100, Chris Angelico wrote:
On Mon, Oct 22, 2018 at 10:16 AM Terry Reedy <tjreedy@udel.edu> wrote:
Except for relatively trivial expressions, this is a bad thing. All functions created from lambda expressions get the same pseudo-name '<lambda>'. This can make tracebacks worse. Perhaps more importantly, proper testing may become harder.
The same considerations bite comprehensions, too, but we don't discourage their use. So I don't think this should be a killer - not on its own, anyhow.
But we *do* discourage large, complex comprehensions, especially nested ones. And more importantly, comprehensions are also limited to a single expression, like lambda, and if you need a multi-statement comprehension we say "turn it into a for-loop".
I do not currently support any proposed syntax for multi-statement lambda functions, mainly because they've all been ugly. But maybe there'll be one, somewhere, some day, that makes sense.
After 25 years, I think the odds of that are pretty slim. -- Steve
On Mon, Oct 22, 2018 at 11:40 AM Steven D'Aprano <steve@pearwood.info> wrote:
On Mon, Oct 22, 2018 at 10:25:19AM +1100, Chris Angelico wrote:
On Mon, Oct 22, 2018 at 10:16 AM Terry Reedy <tjreedy@udel.edu> wrote:
Except for relatively trivial expressions, this is a bad thing. All functions created from lambda expressions get the same pseudo-name '<lambda>'. This can make tracebacks worse. Perhaps more importantly, proper testing may become harder.
The same considerations bite comprehensions, too, but we don't discourage their use. So I don't think this should be a killer - not on its own, anyhow.
But we *do* discourage large, complex comprehensions, especially nested ones. And more importantly, comprehensions are also limited to a single expression, like lambda, and if you need a multi-statement comprehension we say "turn it into a for-loop".
This is true. But nobody ever says "oh but it's impossible to unit-test that for-loop". It's just a block of code within a function, and doesn't NEED to be separately tested. A lambda function should also behave as a subsection of its surrounding code, and thus not need to be unit-tested. I don't know why this is a problem for lambda functions and not for comprehensions or statements. *shrug*
I do not currently support any proposed syntax for multi-statement lambda functions, mainly because they've all been ugly. But maybe there'll be one, somewhere, some day, that makes sense.
After 25 years, I think the odds of that are pretty slim.
Also very true... but I'm trying to be welcoming rather than just rudely say "it hasn't been solved for 25 years so there's no way you could possibly have solved this". After all, we've had plenty of other proposals that took many years to finally happen. Slim is nonzero and I'm open to the possibility that there is now a new syntax that would work. ChrisA
22.10.18 02:16, Terry Reedy пише:
All functions created from lambda expressions get the same pseudo-name '<lambda>'. This can make tracebacks worse. Perhaps more importantly, proper testing may become harder.
See https://bugs.python.org/issue34856. But this can work only while lambda's body is a simple expression.
for i in map(lambda x: x ** 2, 'abc'): print(i)
Traceback (most recent call last): File "<pyshell#19>", line 2, in <module> 2, 'abc'): File "<pyshell#19>", line 2, in <lambda> 2, 'abc'): TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
In 3.8 the traceback is different (an can be even more informative with resolved issue34856). Traceback (most recent call last): File "1.py", line 1, in <module> for i in map(lambda x: x ** File "1.py", line 1, in <lambda> for i in map(lambda x: x ** TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
Given that there might be a hundred functions named '<lambda>', I think the specific name is a bit helpful.
I think the main problem is not with tracebacks, but with reprs. If you have a pack of callbacks, it is not easy to figure out what they do if they are anonymous functions.
it might just be a detail, but Python does not even have single-statement lambdas. The body of a lambda is an expression yielding a value, not a statement. Function languages (from which the idea of the Lambda in Python probably came from) do not have statements at all. Something like the example with "y = x+1; y*2" given earlier is usually expressed as a let-expression, which is syntactic sugar for a lambda itself. Hence, the example could actually be written in Python like so (I am not saying it is beautiful, but just that it is possible ^_^): arr.map( lambda x: (lambda y:y*2)(x+1) ) Or, if you prefer (this seems to me to be syntactically very close to the original): arr.map( lambda x: (lambda y=x+1: y*2)() ) Moreover, in Python 3.8, we will have assignments in expressions, and (even though I obviously can't test this) I wonder if you could then write the same thing as: arr.map( lambda x: (y := x+1, y*2)[1] ) I guess, the original request is therefore not really about having multi-statement lambdas, but more about extending lambdas to anonymous functions with the possibility to have full statements inside the body. Finally, I absolutely agree with that good naming is paramount for understandable code. But IMHO, good naming means that I write a small /named/ function (describing its intent) if its body contains more than just a simple expression, anyway. Cheers, Tobias Quoting Anders Hovmöller <boxed@killingar.net>:
A powerful general purpose language should not limit itself to one statement in a closure.
Nitpick on language: It doesn't. Lambdas are not the only way to do a closure.
It's important to be precise when discussing these things.
Lets add mutli-statement lambdas to python either with just curly braces or with an indent based machine.
Mostly it's just better with a function. Do you have some more compelling examples where you can't use a function smoothly?
/ Anders _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideasCode of Conduct: http://python.org/psf/codeofconduct/
On 10/21/2018 12:06 PM, Andreas Winschu wrote:
There was an extensive discussion about this in the past.
And perhaps 20 other threads, now including this one.
In the end Guido felt that his effort seems to him like building a Rube Goldberg machine.
By looking at the amount of people still stumbling on this issue
What I have seen for 20 years on python-list and perhaps 5 on Stackoverflow is beginners repeatedly stumbling on properly writing and using lambda expressions as they are.
we definitely feel, that it is not the.
?? The royal 'we'??
"Naming things" was from the beginning one the two hardest things in programming.
I completely disagree. I would put naming maybe 20th. Perhaps this explains why you would want anonymous multiline functions while I think they are a foolish idea.
Guido had a hard opinion on this, but i hope the python community does not.
Hah. If I were the new BDFL, I would be tempted to ban further discussion. However, your revelation about how you have a difficulty with naming that I do not helps explain our different viewpoints. -- Terry Jan Reedy
participants (10)
-
Anders Hovmöller
-
Andreas Winschu
-
Chris Angelico
-
Marko Ristin-Kaufmann
-
Ron Reiter
-
Serhiy Storchaka
-
Steven D'Aprano
-
Terry Reedy
-
Tobias Kohn
-
Vladimir Filipović